r03*_*r03 15 javascript reactjs react-hooks
我想将状态保存到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"));
Run Code Online (Sandbox Code Playgroud)
Yan*_*Tay 10
我尝试对useEffect挂钩执行相同的操作,但是在useEffect的返回函数中状态似乎不正确。
其原因是由于关闭。闭包是函数在其范围内对变量的引用。useEffect挂载组件时,您的回调仅运行一次,因此返回回调引用的初始计数值为0。
这里给出的答案就是我的建议。我建议@Jed Richard将答案传递[count]给useEffect,它的作用是localStorage仅在计数更改时才写入。这比在每次更新中完全不传递任何内容的方法要好。除非您非常频繁地更改计数(每隔几毫秒),否则就不会出现性能问题,并且localStorage每当count发生更改时都可以写入。
useEffect(() => { ... }, [count]);
Run Code Online (Sandbox Code Playgroud)
如果您坚持只写localStorage卸载文件,则可以使用一个丑陋的hack /解决方案-参考。基本上,您将创建一个在组件的整个生命周期中都存在的变量,您可以从组件中的任何位置进行引用。但是,您必须手动将状态与该值同步,这非常麻烦。引用不会给您上述关闭问题,因为引用是一个具有current字段的对象,多次调用useRef将返回相同的对象。只要您更改该.current值,您useEffect就可以始终(仅)读取最新的值。
useEffect(() => { ... }, [count]);
Run Code Online (Sandbox Code Playgroud)
const {useState, useEffect, useRef} = React;
function Example() {
const [tab, setTab] = useState(0);
return (
<div>
{tab === 0 && <Content onClose={() => setTab(1)} />}
{tab === 1 && <div>Count in console is not always 0</div>}
</div>
);
}
function Content(props) {
const value = useRef(0);
const [count, setCount] = useState(value.current);
useEffect(() => {
return () => {
console.log('count:', value.current);
};
}, []);
return (
<div>
<p>Day: {count}</p>
<button
onClick={() => {
value.current -= 1;
setCount(value.current);
}}
>
-1
</button>
<button
onClick={() => {
value.current += 1;
setCount(value.current);
}}
>
+1
</button>
<button onClick={() => props.onClose()}>close</button>
</div>
);
}
ReactDOM.render(<Example />, document.querySelector('#app'));Run Code Online (Sandbox Code Playgroud)
您的 useEffect 回调函数显示初始计数,这是因为您的 useEffect 在初始渲染上仅运行一次,并且回调存储有初始渲染期间存在的计数值(为零)。
在你的情况下你会做的是
useEffect(() => {
// TODO: Load state from localStorage on mount
return () => {
console.log("count:", count);
};
});
Run Code Online (Sandbox Code Playgroud)
在反应文档中,您会找到这样定义的原因
React 到底什么时候清理效果?React 在组件卸载时执行清理工作。然而,正如我们之前了解到的,效果会在每次渲染时运行,而不仅仅是一次。这就是为什么 React 还会在下次运行效果之前清除先前渲染的效果。
阅读有关的反应文档Why Effects Run on Each Update
它确实在每个渲染上运行,为了优化它,您可以使其在count更改时运行。但这是当前建议的行为,useEffect正如文档中也提到的那样,并且在实际实现中可能会发生变化。
useEffect(() => {
// TODO: Load state from localStorage on mount
return () => {
console.log("count:", count);
};
}, [count]);
Run Code Online (Sandbox Code Playgroud)
这将起作用 - 使用 React 的 useRef - 但它并不漂亮:
function Content(props) {
const [count, setCount] = useState(0);
const countRef = useRef();
// this effect fires every time count is changed
useEffect(() => () => {
countRef.current = count;
},[count]);
// this effect fires as per a true componentWillUnmount
useEffect(() => () => {
console.log("count:", countRef.current);
}, []);
}
Run Code Online (Sandbox Code Playgroud)
请注意更容易忍受的(在我看来!)'返回函数的函数' useEffect 的代码构造。
问题是 useEffect 在组合时复制了 props 和 state,所以从不重新评估它们 - 这对这个用例没有帮助,但它不是 useEffects 真正的用途。
| 归档时间: |
|
| 查看次数: |
5288 次 |
| 最近记录: |