useEffect的依赖数组默认行为解析

在 React 中,useEffect 的依赖数组默认行为确实有一些需要特别注意的地方,尤其是与"浅比较"相关的问题。以下是关键点的解析:

1. 依赖数组的浅比较机制

useEffect 默认使用 严格相等比较(===) 来检查依赖项是否变化:

useEffect(() => {
  // effect逻辑
}, [dep1, dep2]); // 比较 dep1 === prevDep1 && dep2 === prevDep2

2. 常见陷阱与解决方案

陷阱1:对象/数组的直接传递
const config = { timeout: 1000 };

useEffect(() => {
  const timer = setTimeout(() => {}, config.timeout);
  return () => clearTimeout(timer);
}, [config]); // ❌ 每次渲染都会触发(新对象)

解决方案

// 方法1:拆解基本类型值
useEffect(() => { ... }, [config.timeout]);

// 方法2:useMemo缓存对象
const config = useMemo(() => ({ timeout: 1000 }), []);
陷阱2:函数依赖
const fetchData = () => { /*...*/ };

useEffect(() => {
  fetchData();
}, [fetchData]); // ❌ 除非用useCallback

解决方案

const fetchData = useCallback(() => { /*...*/ }, [deps]);

3. 深度比较的替代方案

如果需要深度比较,可以自定义 hook:

function useDeepCompareEffect(effect, deps) {
  const prevDeps = useRef(deps);
  
  if (!deepEqual(prevDeps.current, deps)) {
    prevDeps.current = deps;
  }

  useEffect(effect, [prevDeps.current]);
}

// 使用 lodash 的 isEqual
import { isEqual } from 'lodash';
const deepEqual = isEqual;

4. 性能优化建议

  1. 依赖项最小化原则

    // 不好
    useEffect(() => { ... }, [props]);
    
    // 好
    useEffect(() => { ... }, [props.id]);
    
  2. 空依赖数组

    useEffect(() => {
      // 仅运行一次的初始化代码
    }, []);
    
  3. 清理函数

    useEffect(() => {
      const subscription = source.subscribe();
      return () => subscription.unsubscribe(); // 清理上次的effect
    }, [source]);
    

5. 为什么React默认使用浅比较?

  1. 性能考量:深度比较可能代价高昂(O(n)复杂度)
  2. 确定性:浅比较行为可预测且稳定
  3. 鼓励不可变数据:推动使用更合理的状态管理方式

实际应用示例

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  // 正确:依赖基本类型值
  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);

  // 需要传递对象时的处理
  const filters = useMemo(() => ({ 
    active: true 
  }), []);

  useEffect(() => {
    applyFilters(filters);
  }, [filters]);

  // ...
}

理解这个机制可以帮助避免不必要的 effect 执行和内存泄漏问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值