javascriptreactjsnext.js

localStorage resets to empty on refresh in NextJS


I have a shopping cart system in my next.js app using Context.

I define my cart with useState:

const [cartItems, setCartItems] = useState([]);

Then I use useEffect to check and update the localStorage:

useEffect(() => {
    if (JSON.parse(localStorage.getItem("cartItems"))) {
      const storedCartItems = JSON.parse(localStorage.getItem("cartItems"));
      setCartItems([...cartItems, ...storedCartItems]);
    }
  }, []);

  useEffect(() => {
    window.localStorage.setItem("cartItems", JSON.stringify(cartItems));
  }, [cartItems]);

This stores the items in localStorage fine, but when I refresh, it resets the cartItems item in localStorage to an empty array. I've seen a few answers where you get the localStorage item before setting the cart state but that throws localStorage is not defined errors in Next. How can I do this?


Solution

  • setCartItems sets the value of cartItems for the next render, so on the initial render it's [] during the second useEffect

    You can fix this by storing a ref (which doesn't rerender on state change) for whether it's the first render or not.

    import React, { useState, useRef } from "react";
    
    // ...
    
    // in component
    
    const initialRender = useRef(true);
    
    useEffect(() => {
        if (JSON.parse(localStorage.getItem("cartItems"))) {
            const storedCartItems = JSON.parse(localStorage.getItem("cartItems"));
            setCartItems([...cartItems, ...storedCartItems]);
        }
    }, []);
    
    useEffect(() => {
        if (initialRender.current) {
            initialRender.current = false;
            return;
        }
        window.localStorage.setItem("cartItems", JSON.stringify(cartItems));
    }, [cartItems]);