useEffect在深层对象属性变更时未触发问题求助
问题根源
React的useEffect默认对依赖项做浅比较,也就是只对比引用地址。你当前的问题大概率是修改sourceArray时直接操作了原数组/原对象的属性,修改后仍把原引用传给setSourceArray,导致React认为状态未发生变更,子组件拿到的groups引用始终不变,自然无法触发useEffect。
解决方案
第一步:修正状态更新逻辑(优先推荐)
遵循React状态更新的不可变原则,每次修改都返回全新的引用,修改后的数组和原数组引用地址不同,此时原有写法的useEffect可直接触发。
示例更新逻辑:
const funcToChangeObj = () => { // 1. 浅拷贝整个数组生成新引用 const changedArray = [...sourceArray] // 2. 找到需要修改的数组项,同样浅拷贝生成新引用 const targetItem = {...changedArray[0]} // 3. 深层属性修改同样遵循不可变原则,比如修改children targetItem.children = [...targetItem.children, {name: '新节点', id: '2'}] // 4. 把修改后的项放回新数组 changedArray[0] = targetItem // 5. 传入新数组更新状态 setSourceArray(changedArray) }
修改完成后原有组件的useEffect就可以正常触发:
function MyComponent({ groups }) { useEffect(() => { // 业务逻辑 }, [groups]) }
第二步:无需修改引用的深比较方案(可选)
如果业务场景确实无法每次更新都生成新的顶层引用,可以自己实现支持深比较的useEffect,不需要引入第三方库:
import { useEffect, useRef, useState } from 'react'; // 深比较函数,普通的对象/数组可以用该实现,存在循环引用、函数、Symbol等类型时可替换为lodash.isEqual function deepEqual(a, b) { try { return JSON.stringify(a) === JSON.stringify(b) } catch(e) { return false } } // 自定义深比较useEffect function useDeepEffect(callback, dependencies) { const prevDeps = useRef(dependencies); const [trigger, setTrigger] = useState(0); // 每次渲染都对比深层值,值变化时更新触发器 if (!deepEqual(prevDeps.current, dependencies)) { prevDeps.current = dependencies; setTrigger(prev => prev + 1); } useEffect(callback, [callback, trigger]); }
在组件中使用:
function MyComponent({ groups }) { useDeepEffect(() => { // 业务逻辑 }, [groups]) }
之前方案失效原因
- useRef方案:你的useEffect依赖仍然是
groups,如果groups引用未发生变化,useEffect完全不会执行,内部的isEqual判断逻辑根本没有运行的机会,自然无效。 - 第三方深比较库方案:大概率是你的状态更新时未改变引用,库拿到的前后值是同一个引用,就算深比较也会认为没有变化,或者库的使用方式不符合要求。
内容的提问来源于stack exchange,提问作者Jeppe Christensen
相关产品推荐
相关产品推荐

