最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - localStorage resets to empty on refresh in NextJS - Stack Overflow

programmeradmin2浏览0评论

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?

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?

Share Improve this question asked Aug 7, 2022 at 21:11 ElendilTheTallElendilTheTall 1,46218 silver badges26 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

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 ponent

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]);

React never updates state immediately It's an asynchronous process, for example, if you console.log(stateValue) just after the setState() method you'll get prevState value in a log. You can read more about it here.

That is exactly happening here, you have called setState method inside the first useEffect, state has not updated yet by react and we're trying to update localStorage with the latest state value(for now it's [] since react has not updated the state yet). that's why the localStorage value holds an empty array.

For your case, you can skip the first execution of 2nd useEffect as @Samathingamajig's mentioned in his answer.

PS: Thanks @Samathingamajig for pointing out the silly mistake, I don't know how missed that. LOL

Another approach that can work:

useLayoutEffect(() => {
    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]);
发布评论

评论列表(0)

  1. 暂无评论