You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中useEffect如何深度比较对象/数组?类组件代码怎么转Hooks?

核心问题解答

1. useEffect依赖项的比较规则

React所有带依赖数组的Hook(useEffect、useMemo、useCallback等)默认都只做浅比较,没有内置深度比较能力。你当前直接把props.settings.config和props.pluginData放到依赖数组里的写法无法达到类组件_.isEqual的效果:如果这两个引用类型的值内部属性完全相同但引用发生变化,会误触发effect;反过来如果引用不变但内部属性修改,effect反而不会触发。

2. 在useEffect中实现深度比较的方案

你可以直接复用已有的_.isEqual能力,有两种常用实现方式:

方案1:结合useRef手动做深度比较

import { useEffect, useRef } from 'react';
import _ from 'lodash';

// 组件内部代码
const prevConfigRef = useRef();
const prevPluginDataRef = useRef();

useEffect(() => {
  const configChanged = !_.isEqual(prevConfigRef.current, props.settings?.config);
  const pluginDataChanged = !_.isEqual(prevPluginDataRef.current, props.pluginData);
  
  // 只有两个值都发生深度变化时才执行业务逻辑,和类组件逻辑完全对齐
  if (configChanged && pluginDataChanged) {
    // 你原来的业务逻辑写在这里
    
    // 更新存储的上一次值
    prevConfigRef.current = props.settings?.config;
    prevPluginDataRef.current = props.pluginData;
  }
}, [props.settings?.config, props.pluginData]);

方案2:封装自定义深度比较Hook(多场景复用)

import { useEffect, useRef } from 'react';
import _ from 'lodash';

// 封装的自定义Hook
const useDeepCompareEffect = (callback, dependencies) => {
  const prevDepsRef = useRef();
  if (!prevDepsRef.current || !_.isEqual(prevDepsRef.current, dependencies)) {
    prevDepsRef.current = dependencies;
  }
  useEffect(callback, [prevDepsRef.current]);
};

// 业务组件直接使用,用法和原生useEffect完全一致
useDeepCompareEffect(() => {
  // 你的业务逻辑
}, [props.settings.config, props.pluginData]);

如果担心lodash体积过大,可以单独引入lodash/isEqual方法,只会引入单个工具函数,体积非常小。

3. 可选属性的处理方式

完全可以沿用类组件的解构赋值加默认值的写法,也可以用可选链?.简化多层嵌套的写法,两种方式都是生效的:

// 和类组件一致的解构写法
const { settings = {} } = props;
const { config = {} } = settings;

// 更简洁的可选链写法
const config = props.settings?.config ?? {};
const pluginData = props.pluginData ?? [];

4. 提供的类组件代码转换为Hooks版本

import { useState, useEffect, useRef } from 'react';
import _ from 'lodash';

const YourComponent = (props) => {
  // 对应类组件的state定义
  const [disabled, setDisabled] = useState(false);
  // 存储上一次的props用于深度比较
  const prevPropsRef = useRef({});

  useEffect(() => {
    // 解构加默认值和类组件写法完全一致
    const {
      settings: { config: { data: { enabled: nextEnabled = false } = {} } = {} } = {},
      pluginData: nextPluginData = []
    } = props || {};

    // 深度判断props没有变化则直接返回,避免重复执行逻辑
    if (_.isEqual(prevPropsRef.current, props)) return;
    prevPropsRef.current = props;

    // 原类组件的业务逻辑直接复用
    if (nextEnabled) {
      if (nextPluginData[0]) {
        setDisabled(!nextPluginData[0][nextEnabled]);
      }
    }
  }, [props.settings?.config?.data?.enabled, props.pluginData]);

  // 剩余组件逻辑...
}

内容的提问来源于stack exchange,提问作者kenanyildiz90

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 12:06:07