useeffect 是 react 函数组件中处理副作用(如数据获取、订阅、定时器、dom 操作等)的核心 hook,它替代了类组件中的生命周期方法,并通过依赖数组精准控制执行时机与清理逻辑。
useeffect 是 react 函数组件中处理副作用(如数据获取、订阅、定时器、dom 操作等)的核心 hook,它替代了类组件中的生命周期方法,并通过依赖数组精准控制执行时机与清理逻辑。
useEffect 是 React Hooks 体系中最关键、也最易被初学者误解的钩子之一。它并非“仅在组件挂载时执行”,而是一个响应式副作用调度器——其行为完全由你传入的回调函数和依赖数组共同决定。
? 三大基本用法与语义
| 依赖数组形式 | 执行时机 | 典型用途 | 注意事项 |
|---|---|---|---|
| useEffect(() => { ... })(无数组) | 每次渲染后都执行(含初始渲染) | 调试日志、强制重绘触发(慎用) | 易引发无限循环,生产环境应避免 |
| useEffect(() => { ... }, [])(空数组) | 仅在组件挂载后执行一次,等价于 componentDidMount | 初始化数据加载、设置全局监听器、启动定时器 | 必须确保内部闭包捕获的值是稳定的(如用 ref 保存最新 state) |
| useEffect(() => { ... }, [a, b])(含依赖) | 组件挂载 + 每当 a 或 b 值变化时重新执行 | 响应 props/state 变化发起 API 请求、动态更新图表 | 依赖必须完整列出所有参与逻辑的变量,否则将导致 stale closure(陈旧闭包)问题 |
✅ 正确使用:带清理的定时器示例
以下是一个经典且安全的 useEffect 实践——实现一个自动递增的计时器,并在组件卸载时自动清除定时器:
React 与 Next.js 性能优化指南,源自 Vercel 工程团队。适用于编写、审查或重构 React/Next.js 代码时使用。
import { useState, useEffect } from 'react';
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
// 启动每秒递增的定时器
const timer = setTimeout(() => {
setCount(prev => prev + 1);
}, 1000);
// 清理函数:组件卸载或 effect 重新执行前调用
return () => clearTimeout(timer);
}, []); // 空依赖数组 → 仅挂载时设置,卸载时清理
return <h1>已渲染 {count} 次!</h1>;
}
export default Timer;? 关键点解析:
- return () => clearTimeout(timer) 是清理函数,React 会在组件卸载前,或下一次 useEffect 回调执行前自动调用它;
- 清理机制确保了资源不泄漏(如未清除的定时器、事件监听器、WebSocket 连接等);
- 即使 count 状态变化触发重渲染,该 effect 也不会重复执行(因依赖为空数组),从而避免了多个定时器叠加。
⚠️ 常见陷阱与最佳实践
- 不要遗漏依赖项:若 effect 中使用了 props.userId 或 state.query,必须将其加入依赖数组,否则逻辑将无法响应最新值;
- 避免空数组下的状态陈旧问题:例如在 [] 中调用 setCount(count + 1) 会始终基于初始 count 值。应改用函数式更新 setCount(c => c + 1),或借助 useRef 保存可变引用;
- 异步操作需校验组件存活:发起 fetch 后,若组件已卸载再调用 setState 会警告并无效。推荐使用 AbortController 或在清理函数中设标志位判断;
- 多个 effect 各司其职:将不同目的的副作用拆分为多个 useEffect(如一个处理数据加载,一个处理事件监听),提升可读性与可维护性。
掌握 useEffect 的本质——“何时运行”由依赖数组声明,“运行什么”由回调定义,“如何收尾”由返回函数保障——你就真正掌握了 React 函数组件中副作用管理的主动权。

















