useEffect 是 React 中用于处理副作用的 Hook,它的第二个参数是一个依赖数组,用于控制 useEffect 的执行时机。根据传入的依赖数组不同,useEffect 的行为也会有所不同。
1. 不传第二个参数
如果 useEffect 没有第二个参数,它会在每次组件渲染后执行。
行为
- 组件挂载时执行。
- 组件每次更新时(包括状态或
props变化)也会执行。
示例
useEffect(() => {
console.log('每次渲染后都会执行');
});
使用场景
- 需要监听所有状态或
props变化的场景(较少使用,因为可能会导致性能问题)。
2. 传空数组 []
如果 useEffect 的第二个参数是一个空数组 [],它只会在组件挂载和卸载时执行。
行为
- 组件挂载时执行。
- 组件卸载时执行清理函数(如果提供了清理函数)。
- 组件更新时不会执行。
示例
useEffect(() => {
console.log('只在组件挂载时执行');
return () => {
console.log('只在组件卸载时执行');
};
}, []);
使用场景
- 只在组件挂载时执行一次的操作(如数据获取、事件监听、定时器等)。
- 需要在组件卸载时清理资源的操作(如移除事件监听、清除定时器等)。
3. 传依赖数组 [deps]
如果 useEffect 的第二个参数是一个包含依赖项的数组 [deps],它会在组件挂载时以及依赖项发生变化时执行。
行为
- 组件挂载时执行。
- 依赖项发生变化时执行。
- 组件卸载时执行清理函数(如果提供了清理函数)。
示例
useEffect(() => {
console.log('组件挂载或 count 变化时执行');
return () => {
console.log('清理函数:组件卸载或 count 变化时执行');
};
}, [count]); // 依赖项是 count
使用场景
- 需要在特定状态或
props变化时执行的操作(如根据props获取数据、监听特定状态的变化等)。
4. 对比总结
| 行为 | 不传第二个参数 | 传空数组 [] | 传依赖数组 [deps] |
|---|---|---|---|
| 组件挂载时执行 | ✅ | ✅ | ✅ |
| 组件更新时执行 | ✅ | ❌ | 仅当依赖项变化时执行 |
| 组件卸载时执行清理函数 | ✅ | ✅ | ✅ |
5. 示例代码
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 不传第二个参数
useEffect(() => {
console.log('每次渲染后都会执行');
});
// 传空数组
useEffect(() => {
console.log('只在组件挂载时执行');
return () => {
console.log('只在组件卸载时执行');
};
}, []);
// 传依赖数组
useEffect(() => {
console.log('组件挂载或 count 变化时执行');
return () => {
console.log('清理函数:组件卸载或 count 变化时执行');
};
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
export default Example;
6. 注意事项
-
依赖项要写全:
- 如果
useEffect中使用了某个状态或props,但没有将其添加到依赖数组中,可能会导致逻辑错误。 - 可以使用 ESLint 规则(如
react-hooks/exhaustive-deps)来检查依赖项是否完整。
- 如果
-
避免无限循环:
- 如果
useEffect中更新了依赖项的状态,且没有正确设置依赖数组,可能会导致无限循环。
- 如果
-
清理函数:
- 如果
useEffect中执行了需要清理的操作(如事件监听、定时器等),务必返回一个清理函数。
- 如果
总结
- 不传第二个参数:每次渲染后都会执行。
- 传空数组
[]:只在组件挂载和卸载时执行。 - 传依赖数组
[deps]:在组件挂载时以及依赖项变化时执行。
根据具体需求选择合适的依赖数组,可以有效控制 useEffect 的执行时机,优化组件性能。