标准答案
- 不传依赖数组时,effect 会在每次渲染提交后执行。
- 传空数组时,effect 在挂载后执行一次;开发严格模式下可能会额外执行一次清理和重建来暴露副作用问题。
- 传依赖数组时,React 会比较依赖值是否变化,变化后先执行上一次 cleanup,再执行新的 effect。
- 依赖应该包含 effect 内部读取的外部变量,避免闭包里使用过期值。
- 常见死循环来自 effect 里 setState,而这个 state 又在依赖数组中,或者依赖了每次 render 都新建的对象、数组、函数。
题目解析
useEffect 适合处理渲染后的副作用,例如请求、订阅、事件监听、同步外部系统。
依赖数组不是随意优化项,而是 effect 和外部变量之间的同步声明。漏依赖可能得到旧值,多依赖或不稳定依赖可能频繁执行。
如果 effect 只是为了从 state 计算另一个 state,通常应该改成派生计算或 useMemo,而不是用 effect 绕一圈。
代码示例
下面的请求 effect 依赖 userId,且在 userId 变化时取消旧请求。
TSX
function UserDetail({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null)
useEffect(() => {
const controller = new AbortController()
fetchUser(userId, { signal: controller.signal })
.then(setUser)
return () => controller.abort()
}, [userId])
return <pre>{JSON.stringify(user, null, 2)}</pre>
}常见误区
- 为了让 effect 少执行,故意漏掉依赖,导致读取旧 props 或旧 state。
- effect 里更新某个 state,同时又依赖这个 state,造成无限循环。
- 把每次渲染都会新建的对象或函数直接放进依赖数组,没有稳定引用或调整逻辑结构。