How to build React Apple Accessories Cart (custom hook and broadcasts, no plugins)
Published: 2022-10-02 19:00:01

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](https://github.com/full-stack-concepts/react-store-hooks-broadcasts)

[See this code in action at Vercel](https://react-store-hooks-broadcasts.vercel.app/)

**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.

```javascript
   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.

```javascript
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.

```javascript
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...

```javascript
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

```javascript
const { contextState: { products } } = useContext( Context );
```

Or you can use the custom hook to grab data.
```javascript
 const [state] = useStore('store');    
 const { orders } = state;
```
And then use it to build a checkout page with @mui (material-ui).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zod33dd0447dhg0weimb.png)