不确定我是否正确使用了 setState,因为我的状态在初始加载期间没有更新

Unsure if I'm using setState correctly because my state is not updated during initial load

我有一个状态可以存储当前应用程序的深色或浅色主题。我使用 useEffect 挂钩将状态更改存储在 localStorage 中,这样当用户刷新页面时,他们的首选主题将被保存,另一个 useEffect 挂钩在初始加载期间更新我的状态。但是,我的状态在初始加载期间不会更新。

我尝试过的事情:

  1. 确保在初始加载期间将 useEffect 放置在之前 useEffect 以存储主题状态
  2. 使用 Promise 确保我的 changeTheme 函数仅在加载状态发生变化后触发
  3. 我记录了我的结果,所以我知道 localStorage 在保存我的状态或跟踪更改方面没有任何问题,但是我的第一个 useEffect 挂钩中的 setState 函数没有正确更新我的状态。

下面是我的代码:

  const [theme, setTheme] = useState({});

  useEffect(() => {
    const loadedTheme = JSON.parse(localStorage.getItem(themeStorage))
    if (loadedTheme) {
      console.log("Initial load")
      console.log(loadedTheme);
      console.log("Setting theme to localStorage")
      Promise.resolve()
        .then(() => { setTheme({ dark: loadedTheme.dark }) })
        .then(() => { console.log(theme) })
        .then(() => changeTheme())
    }
  }, [])

  useEffect(() => {
    localStorage.setItem(themeStorage, JSON.stringify(theme))
    console.log("Inside useEffect to save changed theme")
    console.log(theme)
  }, [theme])

  function changeTheme() {
    console.log("Inside changeTheme function");
    console.log(theme);
    if (!theme || !theme.dark) { //light turns to dark
      for (let vars of Object.keys(varTheme)) {
        document.documentElement.style.setProperty(`--${vars}`, varTheme[vars][0]);
      }
      setTheme({ ...theme, dark: true })
    }
    else {
      for (let vars of Object.keys(varTheme)) { //dark turns to light
        document.documentElement.style.setProperty(`--${vars}`, varTheme[vars][1]);
      }
      setTheme({ ...theme, dark: false })
    }
  }

有点不清楚你认为问题是什么,但我怀疑你是seeing/describingtheme第一个[=14]没有更新=] 勾.

问题

原因是 React 状态被认为是常量和不可变的。在 useEffect 回调中, theme 状态值在回调范围内关闭,无论是等待还是 Promise 链接都会更改在范围内关闭的状态值。

const [theme, setTheme] = useState({}); // <-- initial state

useEffect(() => {
  const loadedTheme = JSON.parse(localStorage.getItem(themeStorage))
  if (loadedTheme) {
    console.log("Initial load")
    console.log(loadedTheme);
    console.log("Setting theme to localStorage")
    Promise.resolve()
      .then(() => { setTheme({ dark: loadedTheme.dark }) })
      .then(() => { console.log(theme) }) // <-- still initial state
      .then(() => changeTheme())
  }
}, []); // <-- mounting "instance" of state

然后您从附件调用 changeTheme,它也将访问附件的“陈旧”状态。

解决方案

LocalStorage 是同步的,因此您可以使用初始化函数从中设置初始状态。

const loadThemeFromStorage = () => {
  const { dark } = JSON.parse(localStorage.getItem(themeStorage)) ?? {};
  return { dark };
};

const [theme, setTheme] = useState(loadThemeFromStorage());

现在状态已经初始设置,您可以安全地在安装 useEffect 挂钩中调用 changeTheme

useEffect(() => {
  console.log("Initial load");
  console.log(theme);
  changeTheme();
}, []);

useEffect(() => {
  localStorage.setItem(themeStorage, JSON.stringify(theme));
  console.log("Inside useEffect to save changed theme");
  console.log(theme);
}, [theme]);