javascriptnext.jslocal-storage

Why does the local storage get deleted when I refresh?


In Next.js I'm using this code:

  const [card, setcard] = useState([]);

  useEffect(() => {
    localStorage.setItem('items', JSON.stringify(card));
  }, [card]);

  useEffect(() => {
    const items = JSON.parse(localStorage.getItem('items'));
    if (items) {
      setcard(items);
    }
  }, []);

I can see that when the value of the card changes, the local storage shows me the value of the card, which is a JSON, so when I refresh, I just see this []

I used this tutorial: https://www.freecodecamp.org/news/how-to-use-localstorage-with-react-hooks-to-set-and-get-items/

How can I fix the code so I don't lose the value of the card after a refresh?

Also, I was using cookies to store the card like this, and it was working, but when the items in my JSON got larger, the cookie didn't store anymore; that's why I want to use local storage.

Example:

  const [card, setcard] = useState(() => eval(getCookie('card') || []));
  setCookie('card', JSON.stringify(card), {
    path: '/',
    maxAge: 60 * 60 * 24,
    HttpOnly: true,
    Secure: true,
  });

Solution

  • The issue is that your first useEffect (with the dependency array [card]) gets triggered before your useEffect hook without any dependency array. You could probably fix it by reversing the order of them, but I would be more thorough than that and only have the one with the [card] dependency array do the save if it sees something other than the default [] that you're supplying. Here's a way to do that:

    // Outside the component
    const defaultCard = [];
    
    function YourComponent() {
        const [card, setCard] = useState(defaultCard);
    
        useEffect(() => {
            const items = JSON.parse(localStorage.getItem("items"));
            if (items) {
                setCard(items);
            }
        }, []);
    
        useEffect(() => {
            if (card !== defaultCard) { // Note we're checking *object identity* here
                localStorage.setItem("items", JSON.stringify(card));
            }
        }, [card]);
        
        // ...
    }
    

    Using object identity makes it so that the code will never save the specific empty array we used as a default (but will store an empty array resulting from the user removing all the cards on purpose).

    Alternatively, don't rely on an effect to save the array. Instead, have a wrapper for setCard:

    function YourComponent() {
        const [card, setCardWorker] = useState(defaultCard);
    
        const setCard = useCallback(
            (card) => {
                localStorage.setItem("items", JSON.stringify(card));
                setCardWorker(card);
            },
            [setCardWorker]
        );
    
        useEffect(() => {
            const items = JSON.parse(localStorage.getItem("items"));
            if (items) {
                setCard(items);
            }
        }, []);
        
        // ...code using `setCard` to update `card` as things are added and removed...
    }
    

    (I've used setCard rather than setcard in these because the vastly common standard is to capitalize the noun after set in these things rather than use all lower-case.