use-context-store
v0.0.2
Published
Creates a context with a redux store, and an easy way to access it via useStore
Downloads
5
Maintainers
Readme
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>