useEffect 的依赖数组为空、有值、不传分别是什么效果?
三种情况
// 1. 不传依赖数组 → 每次渲染后都执行
useEffect(() => {
console.log('每次渲染都执行')
})
// 2. 空数组 [] → 只在挂载后执行一次
useEffect(() => {
console.log('只在挂载后执行一次')
}, [])
// 3. 有依赖值 [a, b] → 依赖变化时执行
useEffect(() => {
console.log('a 或 b 变化时执行')
}, [a, b])详细对比
| 依赖数组 | 执行时机 | 清理函数执行时机 |
|---|---|---|
| 不传 | 每次渲染后 | 下次 effect 执行前 + 卸载时 |
[] |
挂载后一次 | 卸载时 |
[dep] |
挂载后 + dep 变化时 | 下次 effect 执行前 + 卸载时 |
常见陷阱
1. 闭包陷阱(Stale Closure)
function Counter() {
const [count, setCount] = useState(0)
useEffect(() => {
const timer = setInterval(() => {
console.log(count) // ❌ 永远是 0(闭包捕获了初始值)
setCount(count + 1) // ❌ 每次都设置成 1
}, 1000)
return () => clearInterval(timer)
}, []) // count 不在依赖中
// ✅ 解决方案 1:函数式更新
useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1) // 使用函数式更新
}, 1000)
return () => clearInterval(timer)
}, [])
// ✅ 解决方案 2:添加依赖
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1)
}, 1000)
return () => clearInterval(timer)
}, [count]) // 每次都清除并重建定时器
}2. 对象/数组依赖
// ❌ 每次渲染都是新对象,effect 每次都执行
useEffect(() => { /* ... */ }, [{ a: 1 }])
// ✅ 使用原始值
useEffect(() => { /* ... */ }, [obj.a])
// ✅ 或用 useMemo
const dep = useMemo(() => ({ a: 1 }), [a])
useEffect(() => { /* ... */ }, [dep])3. 函数依赖
// ❌ 每次渲染都是新函数
useEffect(() => { handleClick() }, [handleClick])
// ✅ 用 useCallback 包裹
const handleClick = useCallback(() => { /* ... */ }, [dep])
useEffect(() => { handleClick() }, [handleClick])ESLint 规则
react-hooks/exhaustive-deps 会自动检测缺失的依赖,建议开启。