Search code examples
reactjsreact-hooksstatereact-contextshopping-cart

How to initialize a state with use reducer so it don't clear on reload in React


I am working on a react shopping e-commerce website and i am using useContext and useReducer to set my cart state but each time i add to cart and reload the page my cart clears and reinitialize

each time it sets the state on reload it reinitialise it and i need the data to persist

MY CART STATE

const CartState = ({ children }) => {
  let items = [];
  const initialState = {
    showCart: false,
    products: items,
    cartItems: [],
  };
  const { q } = useQuery({
    queryKey: ["products"],
    queryFn: async () => {
      const data = await getDocs(collection(db, "/products"));
      data.docs.forEach((doc) => {
        let key = doc.id;
        let item = doc.data();
        items.push({ key, item });
      });
    },
  });

  const [state, dispatch] = useReducer(CartReducer, initialState);
  const addToCart = (item) => {
    dispatch({ type: ADD_TO_CART, payload: item });
  };
  const showHideCart = () => {
    dispatch({ type: SHOW_HIDE_CART });
  };
  const removeItem = (id) => {
    dispatch({ type: REMOVE_ITEM, payload: id });
  };
  return (
    <ShopContext.Provider
      value={{
        showCart: state.showCart,
        products: state.products,
        cartItem: state.cartItems,
        addToCart,
        showHideCart,
        removeItem,
      }}
    >
      {children}
    </ShopContext.Provider>
  );
};

MY USEREDUCER COMPONENT

import { SHOW_HIDE_CART, ADD_TO_CART, REMOVE_ITEM } from "../types";

const CartReducer = (state, action) => {
  switch (action.type) {
    case SHOW_HIDE_CART: {
      return { ...state, showCart: !state.showCart };
    }
    case ADD_TO_CART: {
      return { ...state, cartItems: [...state.cartItems, action.payload] };
    }
    case REMOVE_ITEM: {
      return {
        ...state,
        cartItems: state.cartItems.filter((item) => item.id !== action.payload),
      };
    }

    default:
      return state;
  }
};

export default CartReducer;

Solution

  • To persist the cart state across page reloads, you can use the browser's localStorage to save the cart data. Here's how you can modify your CartState component to achieve this:

    import React, { useEffect, useReducer } from "react";
    import { getDocs, collection } from "firebase/firestore";
    import { db } from "../firebase";
    import { useQuery } from "react-query";
    import CartReducer from "./CartReducer";
    
    const CartState = ({ children }) => {
      // Load cartItems from localStorage if available, or initialize to an empty array
      const cartItemsFromStorage = localStorage.getItem("cartItems");
      let items = cartItemsFromStorage ? JSON.parse(cartItemsFromStorage) : [];
      
      const initialState = {
        showCart: false,
        products: [], // Assuming this is set by the query and does not need to persist
        cartItems: items,
      };
      
      const { q } = useQuery({
        queryKey: ["products"],
        queryFn: async () => {
          const data = await getDocs(collection(db, "/products"));
          const products = data.docs.map((doc) => ({
            key: doc.id,
            item: doc.data(),
          }));
          return products;
        },
      });
    
      const [state, dispatch] = useReducer(CartReducer, initialState);
    
      // Save cartItems to localStorage whenever it changes
      useEffect(() => {
        localStorage.setItem("cartItems", JSON.stringify(state.cartItems));
      }, [state.cartItems]);
    
      const addToCart = (item) => {
        dispatch({ type: ADD_TO_CART, payload: item });
      };
      const showHideCart = () => {
        dispatch({ type: SHOW_HIDE_CART });
      };
      const removeItem = (id) => {
        dispatch({ type: REMOVE_ITEM, payload: id });
      };
      return (
        <ShopContext.Provider
          value={{
            showCart: state.showCart,
            products: state.products,
            cartItem: state.cartItems,
            addToCart,
            showHideCart,
            removeItem,
          }}
        >
          {children}
        </ShopContext.Provider>
      );
    };
    

    This modification adds a useEffect hook that listens for changes in the cartItems state and saves it to localStorage. It also initializes the cartItems state from localStorage if available. This way, the cart items will persist across page reloads.