hookstores v1.10.1
⛵ hookstores
Hookstores is an elementary Flux implementation using React hooks and Immer.

📦 installation
> npm i --save hookstoresThen wrap your React app with the context Provider:
import React from 'react';
import {render} from 'react-dom';
import {Hookstores} from 'hookstores';
render(
<Hookstores>
<App />
</Hookstores>,
container
);🎨 idea
Hookstoresallows to organize your React app State in one or many stores.- Access local parts of this global state with
hooks - You can listen to specific parts of specific stores, to allow accurate local rendering from your global app state (see the advanced section).
const ItemsContainer = props => {
const {useItemsStore} = useHookstores();
const {items} = useItemsStore();
return <ItemsComponent items={items} />;
};🛠 setup
Here is the path to follow to setup Hookstores on your app:
- 1: write
reactions: the way your store must react to actions. - 2: give your stores to
<Hookstores> - 3: read stores data in your containers
- 4: dispatch actions to trigger
reactions.
In the following, let's illustrate how to use Hookstores with:
- a store of
Itemsand its fetch function - a
containerplugged to this store, - and the
componentrendering the list of items.
Illustration will be marked with 🔍
📦 create a store
You'll have to define a store with
- an initialState,
- and a list of reactions.
const itemsStore = {
initialState: {},
reactions: []
};
export default itemsStore;You should use one store for each feature.
(🔍 here the itemsStore to deal with the Items)
Hookstores will create the actual store for each of them to:
- handle an immutable state with Immer,
- listen to actions to trigger the appropriate
reactions, - emit updates to the containers when there are changes only.

🔆 Reactions
Here is a reaction:
const doSomethingInThisStore = {
on: 'ACTION_TYPE',
reduce: (state, payload) => {
/*
Just update the state with your payload.
Here, `state` is the draftState used by `Immer.produce`
You store will then record your next immutable state.
*/
state.foo = 'bar';
},
perform: (parameters, getState, dispatch) => {
/*
Optional sync or async function.
It will be called before `reduce`
When it is done, reduce will receive the result in
the `payload` parameter.
You can `dispatch` next steps from here as well
*/
}
};A
reactionwill be triggered when an action is dispatched withaction.type===on.reduceis called usingImmer, so mutate thestateexactly as you would with thedraftStateparameter in produce.If you have some business to do before reducing, for example calling an API, use the
performfunction, eithersyncorasync.Then
reducewill be called with the result once it's done.
Here is the example for our illustrating itemsStore
/* ./features/items/store.js */
import apiCall from './fetch-items.js';
const FETCH_ITEMS = 'FETCH_ITEMS';
const initialState = {items: null};
const fetchItems = {
on: FETCH_ITEMS,
perform: async (parameters, getState) => {
// This function will be called whenever {type:FETCH_ITEMS} is dispatched.
// `getState` is provided here for convenience, to access the current store state.
const items = await apiCall(parameters);
return items;
},
reduce: (draft, payload) => {
// 'reduce' will be called after `perform` is over.
// 'perform' returns the items, so here payload === items
draft.items = payload;
}
};
const reactions = [fetchItems];
export default {initialState, reactions};
export {FETCH_ITEMS};🏁 setup the Hookstores provider with these stores
Once all stores are ready, and pass them as stores parameter to <Hookstores>.
This is where you define names your stores.
Hookstores will simply create hooks with the same names with the use prefix.
whatheverNameStore ===> useWhatheverNameStore()🔍 Example:
/* ./index.js */
import React from 'react';
import {render} from 'react-dom';
import {Hookstores} from 'hookstores';
import itemsStore from './features/items/store.js';
import anyOtherStore from './features/whatever/store.js';
const stores = {
itemsStore,
anyOtherStore
};
render(
<Hookstores stores={stores}>
<App />
</Hookstores>,
container
);here Hookstores will create those hooks:
const {useItemsStore, useAnyOtherStore} = useHookstores();🍕 Using those stores in your containers
storeState ➡️ props
Listen to changes in a store and use in your local props by using the useXxxxStore hook that was created for your store.
🔍 Here is the example for our illustrating itemsStore:
/* ./features/items/container.js */
import React from 'react';
import ItemsComponent from './component';
const ItemsContainer = props => {
const {useItemsStore} = useHookstores();
const {items} = useItemsStore();
return <ItemsComponent items={items} />;
};To listen to specific changes in a store, and update your local props only on those changes, use propsMapping (see the advanced section).
📡 dispatching actions
Use prop drilling from your containers to your components: pass functions dispatching the actions
import {SELECT_ITEM} from './features/items/store.js';
const ItemsContainer = props => {
const {dispatch} = useHookstores();
const selectItem = id => () => {
dispatch({
type: SELECT_ITEM,
itemId: id
});
};
return <ItemsComponent selectItem={selectItem} />;
};🔥 advanced usage
The whole point of Hookstores is to be able to perform extremely local rendering.
So, rather than the listening for the whole state updates, you can update rendering depending on specific updates in a store.
To do so, specify the props mapping you want to listen for changes, telling corresponding paths in your store.
const propsMapping = {
items: 'path.to.items.within.your.store',
other: 'plop'
};Now your props will change only when one of these mapping is updated in the store.
const ItemsContainer = props => {
const {useItemsStore} = useHookstores();
const {items, other} = useItemsStore(propsMapping);
return <ItemsComponent items={items} other={other} />;
};This way, on every store update, specific props will be extracted for the components, and nothing else: this will allow accurate local rendering from a global app state.
📚 motivation
read this doc
🏗️ development
local dev tips
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago