This demo shows how you can manage state with a custom hook that implements a reducer to handle changes, context to share data between components and a broadcastchannel to maintain state between components. Actually you can use a single custom hook to create as many sub-stores as you like - for instance one for products, another one for orders etc. Each sub-store can have it's own channel and context. If you want to build a flexible store that maintains state between tabs without using third party plugins then this demo may be useful .
The code for this project can be found on Github
See this code in action at Vercel
Building the Custom Hook
Let's start with coding the store. The store manages state in three different ways: through a reducer store, a context store and local storage. The reducer store handles changes like orders, the context store keeps a copy the state so data can be shared among components and another copy is injected into local storage. So whenever the page refreshes your data is still there.
const [ state, dispatch] = useReducer(storeReducer, initialState);
// backup state for sharing data between components
const { setContextState } = useContext(Context);
// broadcast channel
const channel = new BroadcastChannel( channelName );
You can use the channel to message and listen to incoming posts. Whenever a message arrives you want to update local storage, the context store and reducer store.
channel.onmessage = function(event) {
let { data } = event;
saveStateToLocalStore(data);
dispatch( {type: 'set', value: data });
setContextState(data);
};
Whenever the app loads you want to check is state has been saved in local storage - the backup if anything went wrong. This can be done with the useEffect hook inside our custom hook.
useEffect( () => {
// retrieve state from local storage on load
const cachedState = getStateFromLocalStore();
if(cachedState) {
dispatch({ type: "init", value: cachedState });
setContextState(cachedState);
// add default state to local storage
} else {
saveStateToLocalStore(initialState);
setContextState(initialState);
}
}, [ setContextState ]);
Finally we add some methods and export them. The custom hook looks something like...
export const useStore = (channelName) => {
const [ state, dispatch] = useReducer(storeReducer, initialState);
const { setContextState } = useContext(Context);
const channel = new BroadcastChannel( channelName );
useEffect( () => {
// retrieve state from local storage on load
const cachedState = getStateFromLocalStore();
if(cachedState) {
dispatch({ type: "init", value: cachedState });
setContextState(cachedState);
// add default state to local storage
} else {
saveStateToLocalStore(initialState);
setContextState(initialState);
}
}, [ setContextState ]);
channel.onmessage = function(event) {
let { data } = event;
saveStateToLocalStore(data);
dispatch( {type: 'set', value: data });
setContextState(data);
};
const addOrder = product => { .... }
const removeOrder = (orderID) => {...}
.....
return [
state,
addOrder,
removeOrder,
...
];
}
you can now import the custom hook or context in any component. If you want data, let's say products, you can do
const { contextState: { products } } = useContext( Context );
Or you can use the custom hook to grab data.
const [state] = useStore('store');
const { orders } = state;
And then use it to build a checkout page with @mui (material-ui).
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.