useEffect有依赖项和没有依赖项时return内外的代码执行顺序

最近项目中遇到一个问题,每一秒钟调用一次定时器进行数据请求,在数量达到总数时清除定时器,因为有时候接口会调用失败,所以达不到总数导致定时器停不掉,关闭当前页面去其他页面也停不掉。。所以需要有一个关闭页面就能清除掉定时器的操作,就想到了useEffect第一个函数参数的return。

一、useEffect的基本用法

useEffect 接收两个参数,第一个参数是 Effect 函数;第二个参数是一个数组,避免 effects 不必要的重复调用;
useEffect 第二个参数用于定义所依赖的变量,若传空数组,也就是没有依赖变化的项,只用会在页面初始化时调用一次, 若添加依赖项,依赖数组中变量之一更改,即会触发运行;
第二个参数不存在的时候,每次页面更新都会执行该 useEffect的第一个参数函数;
例如:

import React,{useEffect,useState} from 'react'

function Demo () {
  const [count,setCount]=useState(0)
  useEffect(() => { 
    console.log('无依赖---------------------------',count)
  })
  useEffect(() => { 
    console.log('依赖为[]------------------------',count)
  },[])
  useEffect(() => { 
    console.log('依赖为[count]------------------------',count)
  },[count])
  return (
    <div>
      <p>count的值为: {count} </p>
      <button onClick={()=>setCount(count+1)}>add</button>
    </div>
  )
}

export default Demo

初始化执行结果:

无依赖--------------------------- 0
About.tsx:9 依赖为[]------------------------ 0
About.tsx:12 依赖为[count]------------------------ 0

点击add后的结果:

无依赖--------------------------- 1
About.tsx:12 依赖为[count]------------------------ 1

二、useEffect内return一个函数

关于useEffect() 第一个函数参数的return返回值:

副效应是随着组件加载而发生的,那么组件卸载时,可能需要清理这些副效应。
useEffect()允许返回一个函数,在组件卸载时,执行该函数,清理副效应。如果不需要清理副效应,useEffect()就不用返回任何值。

为了更好的理解,我们先看结果再总结,先看示例补充:

import React,{useEffect,useState} from 'react'

function Demo () {
  const [count,setCount]=useState(0)
  useEffect(() => { 
    console.log('无依赖---------------------------', count)
    return () => {
      console.log('执行  无依赖  时的return的函数')
    }
  })
  useEffect(() => { 
    console.log('依赖为[]------------------------', count)
    return () => {
      console.log('执行 依赖为[]  时的return的函数')
    }
  },[])
  useEffect(() => { 
    console.log('依赖为[count]------------------------', count)
    return () => {
      console.log('执行 依赖为[count]  时的return的函数')
    }
  },[count])
  return (
    <div>
      <p>count的值为: {count} </p>
      <button onClick={()=>setCount(count+1)}>add</button>
    </div>
  )
}

export default Demo

 1. 初始化时控制台打印结果:

无依赖--------------------------- 0
依赖为[]------------------------ 0
依赖为[count]------------------------ 0
执行  无依赖  时的return的函数
执行 依赖为[]  时的return的函数
执行 依赖为[count]  时的return的函数
无依赖--------------------------- 0
依赖为[]------------------------ 0
依赖为[count]------------------------ 0

可以看到初始化时每个useEffect内函数执行了两次,因为我使用的是 React 18, React 18在 并发模式下会强制让组件更新一次,每一个return的函数也在强制更新时执行了一次。相当于执行了如下操作:挂载组件----移除组件–重新挂载组件。如果是React 17 ,只会挂载一次,其打印结果为:

无依赖--------------------------- 0
依赖为[]------------------------ 0
依赖为[count]------------------------ 0

2. 点击add按钮之后的控制台打印结果: 

执行  无依赖  时的return的函数
执行 依赖为[count]  时的return的函数
无依赖--------------------------- 1
依赖为[count]------------------------ 1

此时更新的变量时count,所以监听了count的useEffect都执行了,没有第二个参数的useEffect也执行了,每次都是先执行return的函数,再去执行return外面的部分。

3. 切换到其他页面时(也就是移除组件)控制台打印结果: 

执行  无依赖  时的return的函数
执行 依赖为[]  时的return的函数
执行 依赖为[count]  时的return的函数

此时按照useEffect的顺序依次执行了其内的return部分

useEffect() 的注意点

使用useEffect()时,有一点需要注意。如果有多个副效应,应该调用多个useEffect(),而不应该合并写在一起。

错误写法:

function App() {
  const [varA, setVarA] = useState(0);
  const [varB, setVarB] = useState(0);
  useEffect(() => {
    const timeoutA = setTimeout(() => setVarA(varA + 1), 1000);
    const timeoutB = setTimeout(() => setVarB(varB + 2), 2000);

    return () => {
      clearTimeout(timeoutA);
      clearTimeout(timeoutB);
    };
  }, [varA, varB]);

  return <span>{varA}, {varB}</span>;
}

上面的例子是错误的写法,副效应函数里面有两个定时器,它们之间并没有关系,其实是两个不相关的副效应,不应该写在一起。正确的写法是将它们分开写成两个useEffect()。
正确写法:

function App() {
  const [varA, setVarA] = useState(0);
  const [varB, setVarB] = useState(0);

  useEffect(() => {
    const timeout = setTimeout(() => setVarA(varA + 1), 1000);
    return () => clearTimeout(timeout);
  }, [varA]);

  useEffect(() => {
    const timeout = setTimeout(() => setVarB(varB + 2), 2000);

    return () => clearTimeout(timeout);
  }, [varB]);

  return <span>{varA}, {varB}</span>;
}

总结:

  • 无依赖项时,首次加载会执行useEffect第一个参数函数的return外的部分,每次更新时会先执行return内部分,再执行return外的部分。
  • 依赖项为空数组([])时,会在页面首次加载时运行useEffect 第一个参数的那个函数,类似于执行componentDidMount,且只执行一遍,函数内return 的函数会在页面即将销毁时或移除组件时执行,类似于执行componentWillUnMount
  • 依赖项不为空时,首次加载会执行useEffect第一个参数函数的return外的部分,每次依赖项更新时会先执行return内部分,再执行return外的部分。

也就是说,除了初次挂载和移除组件时会单独执行return外和return内的函数,其余更新的时候都会依次执行return内再执行return外的部分。

原文链接:https://blog.csdn.net/CX345678/article/details/128705404

猜你喜欢

转载自blog.csdn.net/BUG_CONQUEROR_LI/article/details/130875148
今日推荐