分享是人类进步的阶梯
本篇文章重点详细解释
- 函数组件中发送网络请求,在组件卸载的时候,依旧会进行状态的赋值
- 短时间多次发送网络请求,且前面的网络请求返回速度低于后面的返回速度,会将状态设置为旧的状态
看完本篇文章,相信你也能熟练掌握effec的使用了
先看一个🌰:
const [params, setParams] = useState(1);
const [data, setData] = useState(1);
useEffect(() => {
const fetchData = async () => {
const res = await new Promise((resolve) => {
setTimeout(() => {
resolve(params);
}, 8000 / params);
});
setData(res);
};
fetchData();
}, [params]);
return (
<div className="App">
<h1>useEffect的高级使用</h1>
<h2>params: {params}</h2>
<h2>data: {data}</h2>
<button onClick={() => setParams((pre) => ++pre)}>button</button>
</div>
);
可以思考下,当连续点击按钮3下时,data的展示如何变化?
答案如下:
当我们点击button的时候,params的状态发生了改变,组件会发生更新,由于effect的依赖变了,所以里面的函数会重新执行一遍,effect的网络请求用promise + setTimeout 模拟了一下,越早请求的返回越晚,所以状态设置也就越晚了,这就出现有趣的现象了。但一般我们实际想展示的其实是最近一次点击的返回结果,那如何解决呢?
这里会巧妙用到effect的clean up函数,这里先给个官网关于effect中clean up函数的使用🌰
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// 清除订阅
subscription.unsubscribe();
};
});
通常我们会在clean up函数中做副作用的清除,clean up函数会在更新或者卸载的时候执行,更新的时候会先执行clean up函数,再执行传给effect的函数
我们可以将网路请求代码改成如下样子
useEffect(() => {
let didCancel = false
const fetchData = async () => {
const res = await new Promise((resolve) => {
setTimeout(() => {
resolve(params);
}, 8000 / params);
});
!didCancel && setData(res);
};
fetchData();
return () => {
didCancel = true
}
}, [params]);
我们增加了一个变量didCancel来记录状态,在clean up函数中将状态设置为true,只有当状态为false的时候才会执行data状态的赋值。
图例展示button点击三次发生了什么
当params为1的时候,先执行了fetchData,但还没有立马设置值,要等setTimeout结束,这时候params又为2了,它会先执行params为1时所记录的clean up函数,将didCancel设置为true,这样的话,params为1时的fetchData中即使等待setTimeout结束,data的状态也永远不会变了,因为这时候didCancel为true了。同理params为3的时候,将params为2的fetchData中的didCancel设置为true了。
同时,如果在最后一次effect状态赋值之前组件卸载了,这时候会将最后一次effect的didCancel设置为true,那么就不会进行状态赋值了,这样我们就解决了文章开头提出的两个问题,perfect
Over~~~