我在这里做了一个工作示例: https://codesandbox.io/s/magical-flower-o0gyn?file=/src/App.js
当我点击隐藏按钮时,我想将更新的数据保存到本地存储:
setValueWithCallback
运行,将回调设置为引用并设置状态useEffect
开始,使用更新的数据调用回调saveToLocalStorage
在 useCallback
中被调用,data
设置为依赖项问题出在第 3 步,保存到 localstorage 的内容是 {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
运行了回调,为什么它没有在依赖项数组中更新?。
是的,这个例子很奇怪,第二个解决方案非常好,我只是想演示这个问题。
感谢您的帮助!
答案 0 :(得分:2)
您代码中的问题是由于 saveToLocalStorage
函数在组件的本地状态上的闭包。
在 setValueWithCallback
函数中,您使用传递给 saveToLocalStorage
函数的 callback
参数保存对 setValueWithCallback
函数的引用,这就是您的问题所在。
useCallback
钩子将更新 saveToLocalStorage
函数的函数引用,但您不调用该更新后的函数。相反,您调用您在 callbackRef.current
中保存的函数,它不是更新的函数,而是一个旧函数,它对状态的闭包具有 visible
属性值,两个对象中的值都设置为 {{1} }.
您可以通过将 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
钩子内部调用 saveToLocalStorage
函数。
useEffect
答案 1 :(得分:0)
您的 data
是数组类型,只有在状态更新发生时才会响应 shallow comparison
,所以基本上它会检查 data
引用的变化而不是它的值变化。这就是为什么当 data's
值更改时您的 saveToLocalStorage 不会重新启动。