在 React 中,useEffect(() => {}, [])
是一个特殊的副作用钩子函数,其行为取决于第二个参数(依赖数组)。以下是逐层解析和实际场景说明:
一、语法含义
-
无依赖数组:
useEffect(() => {})
每次组件渲染后都会执行副作用函数(包括初次渲染和每次更新),类似类组件中的componentDidMount
+componentDidUpdate
。 -
空依赖数组:
useEffect(() => {}, [])
副作用函数仅在组件初次挂载时执行一次,后续更新不再触发,类似类组件中的componentDidMount
。 -
带依赖项数组:
useEffect(() => {}, [count])
当依赖项(如count
)发生变化时,副作用函数才会执行。
二、useEffect(() => {}, [])
的核心行为
-
执行时机
- 仅在组件挂载到 DOM 后执行一次。
- 组件更新(如 state 或 props 变化)时不会重新执行。
- 若组件卸载,会执行返回的清理函数(如果有)。
-
典型应用场景
useEffect(() => { // 场景 1:初始化数据请求(如获取首屏数据) fetch('/api/data').then(res => setData(res)); // 场景 2:绑定一次性事件监听(如全局键盘事件) window.addEventListener('keypress', handleKeyPress); // 场景 3:执行一次性的第三方库初始化(如地图、图表) const chart = new ChartJS(node, config); // 清理函数(组件卸载时执行) return () => { window.removeEventListener('keypress', handleKeyPress); chart.destroy(); }; }, []); // 空依赖数组
三、常见误区与解决方案
-
依赖项缺失警告
将依赖项加入数组(如
若副作用函数内部使用了 props 或 state,但未声明依赖项,React 会警告:
React Hook useEffect has missing dependencies
解决方案:[userId]
)。若确实只需执行一次,但依赖项变化可能导致逻辑错误,可使用useRef
或函数式更新绕过:const [count, setCount] = useState(0); useEffect(() => { const timer = setInterval(() => { setCount(c => c + 1); // 函数式更新不依赖外部 count }, 1000); return () => clearInterval(timer); }, []); // 无警告
-
异步操作与清理
如果副作用中有异步操作(如请求),需处理组件卸载后可能的内存泄漏:useEffect(() => { let isMounted = true; fetch('/api/data').then(res => { if (isMounted) setData(res); }); return () => { isMounted = false; // 组件卸载时标记为未挂载 }; }, []);
四、对比其他依赖数组形式
依赖数组形式 | 执行时机 | 典型场景 |
---|---|---|
useEffect(() => {}) | 每次渲染后执行 | 响应式 DOM 更新 |
useEffect(() => {}, []) | 仅挂载时执行一次 | 初始化、全局事件绑定 |
useEffect(() => {}, [dep]) | dep 变化时执行 | 数据过滤、依赖项变化的副作用 |
五、总结
useEffect(() => {}, [])
:用于执行一次性的初始化操作,且无需响应后续更新。- 清理函数:必须处理事件解绑、定时器清除等,避免内存泄漏。
- 依赖项规则:遵循 React Hooks 的依赖项规则,必要时使用 ESLint 插件检查。