React 的 useEffect Hook 是处理组件副作用的核心工具,但其依赖数组常引发困惑。理解其机制对写出稳定高效的组件至关重要。
问题场景:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1); // 依赖 count 却未声明
}, 1000);
return () => clearInterval(interval);
}, []); // 空依赖数组
return <div>{
count}</div>;
}
这段代码中,定时器内的 count 始终是初始值 0,导致计数永远显示 1。原因在于 useEffect 仅在挂载时执行一次,其内部闭包捕获了初始的 count 值。
解决方案:正确声明依赖
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1); // ✅ 使用函数式更新
}, 1000);
return () => clearInterval(interval);
}, []); // ✅ 空依赖安全:setCount 函数稳定
或显式声明依赖:
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(interval);
}, [count]); // ✅ 依赖 count 变化
关键点总结:
- 完整性: 依赖数组必须包含所有在 effect 中使用且会变化的 props、state 或上下文值。
- 函数式更新: 当新状态基于旧状态时(如计数),使用
setState(c => c + 1)可避免添加状态依赖。 - 稳定引用: 将对象/函数作为依赖时,确保其引用稳定(如使用
useCallback或useMemo)。 - 无限循环: 在 effect 中直接更新依赖状态会导致循环(除非有终止条件)。仔细设计逻辑。
- 空依赖: 仅当 effect 确实只需运行一次(如初始数据请求)时使用
[]。
正确管理依赖数组是避免陈旧闭包、无限循环和不必要渲染的关键。善用函数式更新和记忆化技术,能写出更清晰、更健壮的 React 组件。