useEffect cleanup函数与依赖比较机制
详细解释React useEffect的清理函数(cleanup function)执行时机和作用,以及React如何比较依赖数组(deps)来决定是否重新执行effect。请举例说明Object.is比较方式导致的潜在问题。
回答
Yahuda
Effect执行与清理时机
useEffect(() => {
// effect逻辑
return () => {
// cleanup函数
};
}, [deps]);
执行顺序:
- 组件挂载 → effect执行
- deps变化 → 先执行上一个effect的cleanup,再执行新effect
- 组件卸载 → 执行最后一个cleanup
cleanup函数作用:
- 取消订阅(取消API请求、清除定时器)
- 移除事件监听
- 重置状态
- 避免内存泄漏和多次effect重叠执行
依赖比较机制
React使用Object.is比较前后两次deps数组中的每个元素:
const prevDeps = [objA, num];
const nextDeps = [objA, num];
Object.is(prevDeps[0], nextDeps[0]) && Object.is(prevDeps[1], nextDeps[1]);
Object.is(类似===但不同点):
Object.is(NaN, NaN)→ true(而NaN !== NaN)Object.is(+0, -0)→ false(而+0 === -0)
潜在问题:
-
引用类型每次渲染新创建
useEffect(() => {}, [{a: 1}]); // 每次渲染都变!对象字面量每次都是新引用 → 死循环
-
函数作为依赖
const fn = () => {}; useEffect(() => fn(), [fn]); // 每次重新创建需要用useCallback包裹
-
数组/对象内联 → 永远触发effect
解决方案:
- 使用useMemo/useCallback稳定引用
- 使用useRef存引用类型
- 比较原始值而非对象
- 使用
JSON.stringify或第三方深度比较(不推荐)