useEffect
传参及其检测时机
在 React 中,useEffect
是一个 Hook,用于处理副作用(side effects),例如数据获取、订阅或手动 DOM 操作等。useEffect
接受两个参数:
- 第一个参数:一个回调函数,包含你希望执行的副作用逻辑。
- 第二个参数(可选):一个依赖项数组(dependency array),用于控制
useEffect
的触发条件。
useEffect
的基本语法
useEffect(() => {
// 副作用逻辑
console.log('副作用逻辑');
// 返回一个清理函数(可选)
return () => {
console.log('清理逻辑');
};
}, [/* 依赖项数组 */]);
依赖项数组的作用
依赖项数组决定了 useEffect
回调函数何时被调用以及是否需要重新执行。具体来说:
- 空数组
[]
:表示useEffect
只会在组件挂载和卸载时执行一次,类似于类组件中的componentDidMount
和componentWillUnmount
。 - 非空数组
[dep1, dep2, ...]
:表示useEffect
会在组件挂载时执行一次,并且在每次依赖项发生变化时重新执行。 - 没有依赖项数组:如果没有提供依赖项数组,
useEffect
将在每次渲染时都执行。
依赖项数组的检测时机
React 会在以下两种情况下检查依赖项数组:
- 组件挂载时:首次渲染时,React 会立即执行
useEffect
回调函数。 - 依赖项变化时:每当依赖项数组中的任何值发生变化时,React 会重新执行
useEffect
回调函数。
useEffect
的执行顺序
为了更好地理解 useEffect
的执行顺序,我们可以将其分为以下几个阶段:
-
初始渲染:
- 组件首次挂载时,React 会执行
useEffect
回调函数。 - 如果
useEffect
返回了一个清理函数,在组件卸载时会执行该清理函数。
- 组件首次挂载时,React 会执行
-
更新渲染:
- 当组件重新渲染时,React 会比较当前依赖项数组与上一次的依赖项数组。
- 如果依赖项数组中的任何一个值发生了变化,React 会先执行上一次
useEffect
返回的清理函数(如果有),然后再次执行useEffect
回调函数。
示例
1. 无依赖项数组
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('useEffect 执行');
});
return (
<div>
<p>计数器: {count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
在这个例子中,useEffect
没有依赖项数组,因此它会在每次组件重新渲染时执行。
2. 空依赖项数组 []
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('useEffect 执行');
return () => {
console.log('useEffect 清理');
};
}, []); // 空数组表示只在挂载和卸载时执行
return (
<div>
<p>计数器: {count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
在这个例子中,useEffect
只会在组件挂载时执行一次,并在组件卸载时执行清理函数。
3. 带有依赖项的数组
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('useEffect 执行');
return () => {
console.log('useEffect 清理');
};
}, [count]); // 只有当 count 发生变化时才会重新执行
return (
<div>
<p>计数器: {count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
在这个例子中,useEffect
只会在组件挂载时执行一次,并且在 count
发生变化时重新执行。
useEffect
的执行顺序
-
挂载时:
- React 首次渲染组件时,会执行
useEffect
回调函数。 - 如果
useEffect
返回了一个清理函数,这个清理函数会在组件卸载时执行。
- React 首次渲染组件时,会执行
-
更新时:
- React 会比较当前依赖项数组与上一次的依赖项数组。
- 如果依赖项数组中的任何一个值发生了变化,React 会先执行上一次
useEffect
返回的清理函数(如果有),然后再次执行useEffect
回调函数。
useEffect
的前检测与后检测
关于“前检测”和“后检测”的概念,实际上可以理解为 useEffect
的执行时机是基于依赖项的变化来决定的。具体来说:
- 前检测:指的是在依赖项发生变化之前执行某些操作。在
useEffect
中,这通常是指执行清理函数。清理函数会在依赖项发生变化之前执行。 - 后检测:指的是在依赖项发生变化之后执行某些操作。在
useEffect
中,这通常是指执行副作用逻辑。副作用逻辑会在依赖项发生变化之后执行。
示例分析
1. 依赖项为空数组 []
useEffect(() => {
console.log('useEffect 执行');
return () => {
console.log('useEffect 清理');
};
}, []);
- 挂载时:
useEffect
回调函数会被执行,输出"useEffect 执行"
。 - 卸载时:清理函数会被执行,输出
"useEffect 清理"
。
2. 依赖项为 [count]
useEffect(() => {
console.log('useEffect 执行');
return () => {
console.log('useEffect 清理');
};
}, [count]);
- 挂载时:
useEffect
回调函数会被执行,输出"useEffect 执行"
。 - 更新时:每当
count
发生变化时,React 会先执行清理函数,输出"useEffect 清理"
,然后再执行useEffect
回调函数,输出"useEffect 执行"
。
总结
- 依赖项数组的作用:
useEffect
的依赖项数组决定了useEffect
回调函数何时执行。如果依赖项数组为空,则useEffect
只会在挂载和卸载时执行;如果依赖项数组不为空,则useEffect
会在挂载时执行一次,并且在依赖项发生变化时重新执行。 - 清理函数的执行时机:清理函数会在依赖项发生变化之前执行,或者在组件卸载时执行。
- 副作用逻辑的执行时机:副作用逻辑会在依赖项发生变化之后执行。
具体执行流程
-
挂载时:
- React 首次渲染组件时,执行
useEffect
回调函数。 - 如果
useEffect
返回了清理函数,React 会在组件卸载时执行该清理函数。
- React 首次渲染组件时,执行
-
更新时:
- React 会比较当前依赖项数组与上一次的依赖项数组。
- 如果依赖项数组中的任何一个值发生了变化,React 会先执行上一次
useEffect
返回的清理函数(如果有),然后再次执行useEffect
回调函数。
关键点总结
- 依赖项数组:决定了
useEffect
回调函数何时执行。 - 清理函数:在依赖项发生变化之前或组件卸载时执行。
- 副作用逻辑:在依赖项发生变化之后执行。
进一步探讨
- 你是否有实际项目中使用过
useEffect
?你觉得它的依赖项数组配置对性能优化有什么影响? - 你是否对
useEffect
的并发模式感兴趣?例如如何结合useEffect
和useRef
来实现更复杂的副作用管理? - 你是否想了解更多关于 React 的生命周期钩子与
useEffect
的对比?它们各自适用于哪些场景?