useEffect的高级使用

360 阅读2分钟

分享是人类进步的阶梯

本篇文章重点详细解释

  1. 函数组件中发送网络请求,在组件卸载的时候,依旧会进行状态的赋值
  2. 短时间多次发送网络请求,且前面的网络请求返回速度低于后面的返回速度,会将状态设置为旧的状态

看完本篇文章,相信你也能熟练掌握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的展示如何变化?

答案如下:

useEffect的缺点.gif

image.png

当我们点击button的时候,params的状态发生了改变,组件会发生更新,由于effect的依赖变了,所以里面的函数会重新执行一遍,effect的网络请求用promise + setTimeout 模拟了一下,越早请求的返回越晚,所以状态设置也就越晚了,这就出现有趣的现象了。但一般我们实际想展示的其实是最近一次点击的返回结果,那如何解决呢?

image.png

这里会巧妙用到effectclean up函数,这里先给个官网关于effectclean 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点击三次发生了什么

Untitled-2022-05-05-1546.png

params为1的时候,先执行了fetchData,但还没有立马设置值,要等setTimeout结束,这时候params又为2了,它会先执行params为1时所记录的clean up函数,将didCancel设置为true,这样的话,params为1时的fetchData中即使等待setTimeout结束,data的状态也永远不会变了,因为这时候didCanceltrue了。同理params为3的时候,将params为2的fetchData中的didCancel设置为true了。

同时,如果在最后一次effect状态赋值之前组件卸载了,这时候会将最后一次effectdidCancel设置为true,那么就不会进行状态赋值了,这样我们就解决了文章开头提出的两个问题,perfect

image.png

Over~~~