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
相关产品推荐
相关产品推荐

