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