目录
1,介绍
作用
用于在函数组件内,处理副作用,比如:
- ajax 请求
- 计时器
- 其他异步操作
- 更改真实DOM对象
- 本地存储
- 其他会对外部产生影响的操作(和自身无关)。
介绍
useEffect
接收2个参数,
- 参数1,一个函数。在该函数中执行副作用。
- 参数2(可选),一个数组。记录参数1中使用到的依赖。
js
import React, { useEffect, useState } from "react";
export default function App() {
const [n, setN] = useState(0);
useEffect(() => {
document.title = `数字:${n}`;
});
return (
<div>
<span>{n}</span>
<button
onClick={() => {
setN(n + 1);
}}
>
加一
</button>
</div>
);
}
2,注意点
2.1,参数1,副作用函数
2.1.1,运行时间点
在页面完成真实UI渲染后执行,并且是异步的,不会阻塞浏览器。
和类组件中的生命周期函数 componentDidMount
和 componentDidUpdate
的区别:
它们是同步 执行的;在得到真实DOM后,用户还没有看到真实UI渲染时执行。
2.1.2,返回值
要么没有返回值,如果有必须是一个函数。该函数称为清理函数。
js
type EffectCallback = () => void | (() => void);;
清理函数的特点:
- 首次渲染不会执行,组件销毁一定执行;
- 再次渲染时,会在副作用函数之前运行。
所以,一般会在清理函数中,清理副作用函数开启的计时器。
js
import React, { useEffect, useState } from "react";
function Child() {
useEffect(() => {
let timer = null;
let n = 10;
timer = setInterval(() => {
console.log(n--);
}, 1000);
return () => {
clearInterval(timer);
};
});
return <div>子组件</div>;
}
export default function App() {
const [visible, setVisible] = useState(true);
return (
<div>
{visible && <Child />}
<button
onClick={() => {
setVisible(!visible);
}}
>
显示/隐藏
</button>
</div>
);
}
2.1.3,闭包的影响
在副作用函数中,如果使用了函数组件上下文中的变量,受闭包影响,会导致副作用函数中的变量不会实时变化。
原理和闭包的经典问题一样,不同点是这里的 n 是一个状态,
也就是说,每次函数组件重新调用,计时器的回调函数使用的都是当次函数组件调用时产生的新值( 相当于计时器中使用了传递的参数)。
所以每次开启新计时器时,n 都不一样。
js
export default function App() {
const [n, setN] = useState(10);
useEffect(() => {
setTimeout(() => {
console.log(n);
}, 3000);
});
return (
<div>
<button
onClick={() => {
setN(n - 1);
}}
>
n-1
</button>
</div>
);
}
3s 后打印,所以在3s内连续点击按钮3次,3s后会先打印出 10
(首次渲染),再依次打印出 9 8 7
。
2.1.4,严禁出现在代码块中(判断,循环)
原理和 useState
类似,这样写会导致无法正确获取要执行的副作用函数。
2.1.5,尽量保持副作用函数稳定
副作用函数在每次(重新渲染)注册时,会覆盖掉之前的副作用函数,因此,尽量保持副作用函数稳定,否则控制起来会比较复杂。
2.2,参数2,数组
作用 :记录副作用函数的依赖数据。当组件重新渲染后,如果新旧依赖数据不一致(Object.is()
判断),才会执行副作用函数。
类似于 Vue3 中的 watcheffect。
所以,如果依赖数据没有发生变化,则:
- 副作用函数仅在组件第一次渲染后执行;
- 清理函数仅在组件卸载时执行。
参数2传递空数组就能实现依赖不发生变化。
js
useEffect(() => {
console.log('副作用函数执行');
return () => {
console.log('清理函数执行');
}
}, [])
比如请求列表数据,当前页码 page
和每页展示个数 limit
发生变化时,再请求接口:
js
useEffect(() => {
(async function () {
const resp = await getList(page, limit);
// 获取 list 和 total
})();
}, [page, limit])
以上。