在 React useEffect hook 中引用过时的状态
当组件卸载时,我想将状态保存到
localStorage
。
这曾经在
componentWillUnmount
中起作用。
我尝试使用
useEffect
钩子执行相同操作,但似乎
useEffect
的返回函数中的状态不正确。
为什么会这样?如何在不使用类的情况下保存状态?
这是一个虚拟示例。当您按下关闭时,结果始终为 0。
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
function Example() {
const [tab, setTab] = useState(0);
return (
<div>
{tab === 0 && <Content onClose={() => setTab(1)} />}
{tab === 1 && <div>Why is count in console always 0 ?</div>}
</div>
);
}
function Content(props) {
const [count, setCount] = useState(0);
useEffect(() => {
// TODO: Load state from localStorage on mount
return () => {
console.log("count:", count);
};
}, []);
return (
<div>
<p>Day: {count}</p>
<button onClick={() => setCount(count - 1)}>-1</button>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => props.onClose()}>close</button>
</div>
);
}
ReactDOM.render(<Example />, document.querySelector("#app"));
792530951
原因是由于关闭。闭合是函数对其范围中变量的引用。您的
useffect
回调仅在组件安装时运行一次,因此返回回调是引用0.
我建议的答案。我建议@Jed Richard通过
[count]
使用
的答案,该
仅在计数更改时将写入
localStorage
。这比在每次更新中都没有写任何东西的方法要好。除非您经常更改计数(每几个MS),否则您不会看到性能问题,并且在
localStorage
count
count
更改时,写入
localStorage
可以。
602772730
如果您只坚持在Unmount上写入
localstorage
,则可以使用一个丑陋的hack/解决方案-Refs。基本上,您将创建一个在组件的整个生命周期中都存在的变量,您可以从其内部的任何地方引用该变量。但是,您必须手动将您的状态与该价值同步,这非常麻烦。参考文献不给您上述关闭问题,因为ref是一个具有
当前
字段的对象,并且多个调用
useref
将返回您的对象。只要您突变
.current
值,您的
useffect
始终可以(仅)读取最新的值。
465300021
这将起作用 - 使用 React 的 useRef - 但效果并不好:
function Content(props) {
const [count, setCount] = useState(0);
const countRef = useRef();
// set/update countRef just like a regular variable
countRef.current = count;
// this effect fires as per a true componentWillUnmount
useEffect(() => () => {
console.log("count:", countRef.current);
}, []);
}
请注意 useEffect 的“返回函数的函数”代码构造稍微更可忍受(在我看来!)。
问题是 useEffect 在组合时复制 props 和状态,因此永远不会重新评估它们 - 这对这个用例没有帮助,但这不是 useEffects 的真正用途。
感谢 @Xitang 将 ref 直接分配给 .current,这里不需要 useEffect。太棒了!
您的 useEffect 回调函数显示初始计数,这是因为您的 useEffect 在初始渲染时仅运行一次,并且回调存储了初始渲染期间存在的计数值,该值为零。
在您的情况下,您应该做的是
useEffect(() => {
// TODO: Load state from localStorage on mount
return () => {
console.log("count:", count);
};
});
在 React 文档中,您会找到为什么这样定义的原因
When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time.
阅读 React 文档
为什么 Effects 会在每次更新时运行
它确实在每次渲染时运行,为了优化它,您可以让它在
count
更改时运行。但这是
useEffect
当前提议的行为,正如文档中提到的那样,在实际实施中可能会发生变化。
useEffect(() => {
// TODO: Load state from localStorage on mount
return () => {
console.log("count:", count);
};
}, [count]);