skip to Main Content

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,
  });

3

Answers


  1. When your page reloads, the cards are set to [] and the useEffect hook gets called so local storage’s items is set to [].

    You need to check whether card has length or not and then set.

    const [card, setcard] = useState([]);
    
    useEffect(() => {
      if (card.length) {
        localStorage.setItem('items', JSON.stringify(card));
      }
    }, [card]);
    
    useEffect(() => {
      const items = JSON.parse(localStorage.getItem('items'));
      if (items) {
        setcard(items);
      }
    }, []);
    
    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
  3. The main issue is that your initial card value is [], regardless of the contents of items in localStorage.

    Instead of using [], you should first check localStorage. If there there is no value for items, use your default []. However if there is a value present, use that as initial value instead.

    const [card, setcard] = useState(() => {
      const json = localStorage.getItem('items');
      if (!json) return []; // default card
      return JSON.parse(json);
    });
    
    useEffect(() => {
      localStorage.setItem('items', JSON.stringify(card));
    }, [card]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search