如何让useEffect在WatermelonDB返回的深度嵌套对象数组变化时触发?
解决WatermelonDB深度嵌套数组变化时useEffect不触发的问题
问题原因
React的useEffect默认对依赖项进行浅比较,而WatermelonDB返回的arrayWithDeeplyNestedObjects数组引用可能不会变化(即使内部对象的属性或数组长度改变),导致浅比较认为依赖项未更新,从而不触发副作用。
解决方案
方法1:基于WatermelonDB Record特性生成精准依赖项
利用WatermelonDB Record对象的_raw属性(保存原始数据),提取关键变化字段生成能反映内容更新的依赖项,让浅比较能识别变化:
const Foo = ({ arrayWithDeeplyNestedObjects }) => { console.log('render'); // 生成包含关键变化字段的依赖数组 const effectDependency = arrayWithDeeplyNestedObjects.map(item => `${item.id}-${item._raw.x}-${item._raw.y}-${item._raw.id_arr}` ); useEffect(() => { const doSomething = async () => { // 你的异步逻辑 }; (async () => { const data = await doSomething(); setData(data); })(); console.log(new Date()); }, [effectDependency]); // 依赖项随内部属性变化自动更新 return <SomeNiceUi />; };
如果需要跟踪更多字段,也可以用JSON.stringify序列化关键数据:
const effectDependency = arrayWithDeeplyNestedObjects.map(item => JSON.stringify({ id: item.id, x: item._raw.x, y: item._raw.y, id_arr: item._raw.id_arr }) );
方法2:自定义深比较useEffect
实现一个支持深比较的Hook,替代原生useEffect,直接监听深度嵌套数组的变化:
import { useEffect, useRef } from 'react'; // 针对你的数组结构实现深比较函数 const isArrayChanged = (arr1, arr2) => { if (!arr1 || !arr2 || arr1.length !== arr2.length) return true; return arr1.some((item, index) => { const raw1 = item._raw; const raw2 = arr2[index]._raw; return raw1.id !== raw2.id || raw1.x !== raw2.x || raw1.y !== raw2.y || raw1.id_arr !== raw2.id_arr; }); }; const useDeepCompareEffect = (effect, dependencies) => { const prevDepsRef = useRef(); useEffect(() => { if (isArrayChanged(prevDepsRef.current, dependencies[0])) { effect(); prevDepsRef.current = dependencies[0]; } }); }; // 使用自定义Hook const Foo = ({ arrayWithDeeplyNestedObjects }) => { console.log('render'); useDeepCompareEffect(() => { const doSomething = async () => { // 你的异步逻辑 }; (async () => { const data = await doSomething(); setData(data); })(); console.log(new Date()); }, [arrayWithDeeplyNestedObjects]); return <SomeNiceUi />; };
方法3:利用组件重渲染特性跟踪变化
由于组件已经会在数据库更新时重新渲染,可以用useRef保存上一次的数组状态,在每次渲染时检查深度变化:
import { useEffect, useRef } from 'react'; const isArrayChanged = (arr1, arr2) => { if (!arr1 || !arr2 || arr1.length !== arr2.length) return true; return arr1.some((item, index) => { const raw1 = item._raw; const raw2 = arr2[index]._raw; return raw1.id !== raw2.id || raw1.x !== raw2.x || raw1.y !== raw2.y || raw1.id_arr !== raw2.id_arr; }); }; const Foo = ({ arrayWithDeeplyNestedObjects }) => { console.log('render'); const prevArrayRef = useRef(); useEffect(() => { if (isArrayChanged(arrayWithDeeplyNestedObjects, prevArrayRef.current)) { const doSomething = async () => { // 你的异步逻辑 }; (async () => { const data = await doSomething(); setData(data); })(); console.log(new Date()); } prevArrayRef.current = arrayWithDeeplyNestedObjects; }); // 无依赖项,每次渲染都执行,但仅在数组变化时触发逻辑 return <SomeNiceUi />; };
方案选择
- 若仅关心特定字段变化,方法1性能最优且实现简单;
- 若需要通用的深比较场景,方法2更灵活;
- 若不想额外处理依赖项,方法3可直接利用组件重渲染特性。
内容的提问来源于stack exchange,提问作者four-eyes
相关产品推荐
相关产品推荐

