DEV Community

Cover image for How to build React Apple Accessories Cart (custom hook and broadcasts, no plugins)
full-stack-concepts
full-stack-concepts

Posted on • Updated on

How to build React Apple Accessories Cart (custom hook and broadcasts, no plugins)

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 );     
Enter fullscreen mode Exit fullscreen mode

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);
};       
Enter fullscreen mode Exit fullscreen mode

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 ]);

Enter fullscreen mode Exit fullscreen mode

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,
    ...    
  ];
}
Enter fullscreen mode Exit fullscreen mode

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 );
Enter fullscreen mode Exit fullscreen mode

Or you can use the custom hook to grab data.

 const [state] = useStore('store');    
 const { orders } = state;
Enter fullscreen mode Exit fullscreen mode

And then use it to build a checkout page with @mui (material-ui).

Image description

Top comments (0)