Search code examples
javascriptreactjslocal-storagereact-context

React: Setting state to local storage


Am requesting help setting the cartItems state to local storage so that when never I refresh, I still get the items.

import React, { createContext, useState } from 'react';

export const AppContext = createContext();

export function Context(props) {
  const [cartItems, setCartItems] = useState([]);

  const cart = (item) => {
    const exist = cartItems.find((x) => x.id === item.id);
    if (!exist) {
      setCartItems([...cartItems, { ...item }]);
    } else {
      alert('Item Already Taken');
    }
  };

  const onAdd = (items) => {
    console.log(cartItems);
    const exist = cartItems.find((x) => x.id === items.id);
    if (exist) {
      setCartItems(
        cartItems.map((x) =>
          x.id === items.id ? { ...exist, qty: exist.qty + 1 } : x,
        ),
      );
      console.log(cartItems);
    } else {
      console.log(items);
      setCartItems([...cartItems, { ...items, qty: 1 }]);
    }
  };

  const onRemove = (product) => {
    const exist = cartItems.find((x) => x.id === product.id);
    if (exist.qty === 1) return;
    setCartItems(
      cartItems.map((x) =>
        x.id === product.id ? { ...exist, qty: exist.qty - 1 } : x,
      ),
    );
  };

  const onDeleted = (product) => {
    if (window.confirm('Do you want to delete this product?')) {
      setCartItems(cartItems.filter((x) => x.id !== product.id));
    }
  };

  const itemPrice =
    cartItems && cartItems.reduce((a, c) => a + c.qty * c.price, 0);
  const delieveryPrice = '3000';
  // eslint-disable-next-line
  const totalPrice =
    parseInt(itemPrice) + (itemPrice && parseInt(delieveryPrice));

  return (
    <AppContext.Provider
      value={{
        cartItems,
        setCartItems,
        onAdd,
        onRemove,
        cart,
        onDeleted,
        itemPrice,
        totalPrice,
        delieveryPrice,
      }}
    >
      {props.children}
    </AppContext.Provider>
  );
}

Solution

  • You can acheive your goal by creating a customHook to initialize state with value in localStorage and alos write state on the localStorage on every update, like this:

    import * as React from 'react'
    function useLocalStorageState(
      key,
      defaultValue = '',
      {serialize = JSON.stringify, deserialize = JSON.parse} = {},
    ) {
      const [state, setState] = React.useState(() => {
        const valueInLocalStorage = window.localStorage.getItem(key)
        if (valueInLocalStorage) {
          try {
            return deserialize(valueInLocalStorage)
          } catch (error) {
            window.localStorage.removeItem(key)
          }
        }
        return typeof defaultValue === 'function' ? defaultValue() : defaultValue
      })
    
      const prevKeyRef = React.useRef(key)
    
      React.useEffect(() => {
        const prevKey = prevKeyRef.current
        if (prevKey !== key) {
          window.localStorage.removeItem(prevKey)
        }
        prevKeyRef.current = key
        window.localStorage.setItem(key, serialize(state))
      }, [key, state, serialize])
    
      return [state, setState]
    }
    

    and use it in your component like this:

    const [cartItems, setCartItems] = useLocalStorageState('cartItems',[]);