0.0.2 • Published 5 years ago

use-context-store v0.0.2

Weekly downloads
2
License
MIT
Repository
github
Last release
5 years ago

use-context-store

Easily create Redux stores inside of a React context, and access them with a useReducer-style hook

Install

npm i use-context-store

Usage

Create the Context Store

Use the createContextStore() function to create a Redux store inside of a React context. It returns an object containing a <StoreProvider />, a useStore() hook, and a connectStore() HOC.

// shopping-cart-store.js

import createContextStore from 'use-context-store'
import shoppingCartReducer from './reducer.js'

const defaultState = { items: [] }

const shoppingCartStore = createContextStore(shoppingCartReducer, defaultState)

export default {
    useShoppingCart: shoppingCartStore.useStore,
    ShoppingCartProvider: shoppingCartStore.StoreProvider
}

Provide the Store Context to your Components

Use the <StoreProvider /> we just created to... provide the store to any children components.

// app.jsx

import React from 'react'
import { ShoppingCartProvider } from '../store/shopping-cart/shopping-cart-store'
import MainContent from './main-content'
import CartSidebar from './cart-sidebar'

const MyApp = () => 
    <ShoppingCartProvider>
        <div className="main">
            <MainContent />
            <CartSidebar />
        </div>
    </ShoppingCartProvider>

Use the Store in your Child Components

The useStore Way

You can use the useStore() hook we created (we renamed it useShoppingCart).

// MainContent.jsx

import React from 'react'
import { useShoppingCart } from '../store/shopping-cart/shopping-cart-store'

const MainContent = () => {
    const [ state, dispatch ] = useShoppingCart()

    return (
        <div>
            {
                state.items.length > 10 &&
                <span>Enough shopping, just buy the stuff already</span>
            }
            {/* ...some other main content stuff... */}
        </div>
    )
}
// CartSidebar.jsx

import React from 'react'
import { useShoppingCart } from '../store/shopping-cart/shopping-cart-store'
import { beginCheckout, clearCart } from '../store/shopping-cart/action-creators'

export const CartSidebar = ({ items, beginCheckout, clearCart }) =>
    <div>
        <ul>
            { items.map(item => <Item {...item} />) }
        </ul>
        <span>{getSubtotal(items)}</span>
        <button onClick={clearCart}>Clear Cart</button>
        <button onClick={beginCheckout}>Checkout</button>
    </div>

const ConnectedCartSidebar = () => {
    const [state, dispatch] = useShoppingCart()

    return <CartSidebar
        items={state.items}
        beginCheckout={() => dispatch(beginCheckout())
        clearCart={() => dispatch(clearCart())
    />
}

export default ConnectedCartSidebar;

The connectStore Way

I'm still going back and forth on whether or not I want a connectStore HOC (see Redux connect). On the one hand, it works really great if you are only connecting one store, and you have a bunch of actions (it just maps the dispatch for you to all of the actions, so in your inner component you can just call them directly from props and it takes away the (...args) => dispatch(actionCreator(...args)) boilerplate). It also makes testing less questionable, since you end up with a pure component with no hooks or logic in it. Connecting multiple stores to one component doesn't work great with a connect HOC - in those cases, the useStore hook is better for sure.


Composing multiple Provier wrappers

This looks bad 👎

const MyApp = () => 
    <UserProvider>
        <ShoppingCartProvider>
            <OtherProvider>
                <KewlKontextProvider>
                    <div className="main">
                        <MainContent />
                        <CartSidebar />
                    </div>
                </KewlKontextProvider>
            </OtherProvider>
        </ShoppingCartProvider>
    </UserProvider>

This looks better 👍

const providers = [ 
    UserProvider, 
    ShoppingCartProvider, 
    OtherProvider, 
    KewlKontextProvider
];

const MainStoreProvider = compose(providers)

const MyApp = () => 
    <MainStoreProvider>
        <div className="main">
            <MainContent />
            <CartSidebar />
        </div>
    </MainStoreProvider>