带回调的 useState 不更新 useCallback 依赖项

useState with callback not updating useCallback dependencies

我在这里做了一个工作示例: https://codesandbox.io/s/magical-flower-o0gyn?file=/src/App.js

当我点击隐藏按钮时,我想将更新的数据保存到本地存储:

  1. 我在第一列上单击隐藏:setValueWithCallback 运行,将回调设置为 ref 并设置状态
  2. useEffect 启动,使用更新后的数据调用回调
  3. saveToLocalStorageuseCallback 中被调用,data 被设置为依赖项

问题出在第 3 步,两者保存到本地存储的内容都是 {visible: true}。 我知道如果我改变这一行:

const saveToLocalStorage = useCallback(() => {
  localStorage.setItem(
    `_colProps`,
    JSON.stringify(data.map((e) => ({ id: e.id, visible: e.visible })))
  );
}, [data]);

为此:

const saveToLocalStorage = localStorage.setItem(
  `_colProps`,
  JSON.stringify(data.map((e) => ({ id: e.id, visible: e.visible })))
);

它有效,但我无法理解,为什么它不能与第一个一起使用。我认为它一定是某种封闭的东西,但我没有看到它。

如果data已经更新,useEffect运行回调,为什么不在依赖数组中更新?。 是的,这个例子很奇怪,第二个解决方案非常好,我只是想证明这个问题。 感谢您的帮助!

您的 data 是数组类型,并且仅在发生状态更新时做出 shallow comparison 反应,因此基本上它会检查 data 引用更改而不是其值更改。这就是为什么当 data's 值更改时您的 saveToLocalStorage 没有重新启动。

您的代码中的问题是因为 saveToLocalStorage 函数关闭了组件的本地状态。

setValueWithCallback 函数中,您使用传递给 setValueWithCallback 函数的 callback 参数保存对 saveToLocalStorage 函数的引用,这就是您的问题所在是。

useCallback 挂钩将更新 saveToLocalStorage 函数的函数引用,但您 不要 调用更新后的函数。相反,您调用保存在 callbackRef.current 中的函数,它不是更新的函数,而是一个旧函数,它在两个对象中的值都为 visible 属性 的状态上有一个闭包设置为true.

解决方案

您可以通过将 data 作为参数传递给回调函数来解决此问题。您在调用 callbackRef.current(data) 时已经传递了参数,但您没有在 saveToLocalStorage 函数中使用它。

更改 saveToLocalStorage 以使用从 useEffect 挂钩内部传递的参数。

const saveToLocalStorage = useCallback((updatedData) => {
    localStorage.setItem(
      `_colProps`,
      JSON.stringify(updatedData.map((e) => ({ id: e.id, visible: e.visible })))
    );
}, []); 

解决这个问题的另一种方法是摆脱 callbackRef 并从 useEffect 钩子内部调用 saveToLocalStorage 函数。

useEffect(() => {
    saveToLocalStorage();
}, [data, saveToLocalStorage]);