useEffect
作用:
-
推迟代码执行
渲染完成之后再执行,常用于 数据读取 -
完成移除 - 组件卸载的时候做一些清理的工作
例如:移除定时器、事件监听器等
有关组件卸载:
触发时机:当组件从 DOM 中移除时,会触发useEffect里返回的函数,useEffect里的内容不会执行,执行也没有意义了。当组件出现在DOM中时,会执行useEffect里的代码,useEffect里返回的函数不执行。
import React, { useState, useEffect } from 'react';
function TimerComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(prevCount => prevCount + 1);
}, 1000);
// 返回的函数将在组件卸载时执行
return () => {
clearInterval(timer);
console.log('Timer cleared');
};
}, []); // 空依赖数组,effect 只在挂载和卸载时运行
return <div>Timer: {count}</div>;
}
export default TimerComponent;
该行为是模拟了componentWillUnmount
,常用于清理操作,例如取消网络请求、清除计时器或移除事件监听器。
补充:何时执行componentWillUnmount?
-
组件从页面移除:当用户导航到另一个页面或条件渲染使得组件不再显示时,componentWillUnmount 会被调用。在上面的例子中,当 TimerComponent 被移除时,计时器会被清除。
-
父组件卸载:如果父组件卸载了,所有子组件也会卸载,它们的 componentWillUnmount 也会被调用。
- 第二个参数来模拟生命周期。
对于缓存的对象来说,因为每次函数都重新执行,因此对象的引用会变,所以useEffect第二个参数缓存的是引用的地址。
useEffect存在一种bail out
(保释) / (补救)机制:
当你尝试设置相同的状态,仅仅让你把函数执行完,但不会再触发effect。
同理,在class组件中的setState也是一样。
setState
state设置重复的值 —— 只会重复渲染一次 +1
1. 推迟代码执行
例如:

上面其实本来就是一种错误的写法,只是React官方帮你兜了个底。
正确的做法是:
2. 完成移除 定时器、监听
function HOC() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('创建')
return () => {
console.log('销毁')
}
});
return(
<>
<button onClick={() => setCount(count + 1)}>按钮</button>
</>
)
}
export default HOC;
当状态变化的时候,重新渲染,重新渲染导致useEffect执行(前提是没有依赖项)。在第一次刷新的时候,不执行useEffect返回的函数,只执行useEffect里的内容。后面状态变化导致重新渲染的时候,会优先执行useEffect返回的函数,然后再执行useEffect里的内容
。
例如:
页面初始化时:
第一次点击按钮时:
useEffect - clean-up阶段有两个:
- 删除组件
- 重新渲染当前组件
1. 第一个,删除组件的时候执行:
- 注意:
- useEffect里的回调如果有返回值的话,必须是一个函数。
- 会先执行这个return函数,例如下面的代码。
2. 第二个,重新渲染的时候执行:
上面两种情景这样做的官方总结:
react官方:
1.必要性:极大的提升程序的可靠性
2.函数组件:简单、小型组件
程序可靠性
程序性能
结论:重新渲染的时候就先移除然后重新添加,目的是为了增强稳定性,如果有很庞大的初始化、移除操作,还应该用class(didMount、willUnmount)
注意:
错误的使用state,会导致资源泄露
例如:
1.某种异步操作开始——非常长
2.组件已经被卸载了
3.setState - 资源(内存泄露、socket泄露,主要是内存泄露)泄露
代码如下:
会报如下警告⚠️:
Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
in Unknown (at App.js:13)
解决方法:
3. 第三种, 第二个参数来模拟生命周期。
useEffect选择执行:
当a变化,才打印effect a;
当b变化,才打印effect b;
类似于 [a]
这种形式 官方叫做 ”跳过“
useEffect模拟生存周期函数:
useEffect(fn, [值]);
如:componentDidMount
useEffect() 的callback返回函数的case,其实是根据当前操作来的,是添加移除定时器还是添加移除dom,执行效果是不一样的,具体可以参考useEffect() 有始有终