React Hooks useDidUpdateEffect如何实现深度比较避免不必要执行
解决方案
你有两种无需开发新钩子的方案可直接落地,均能解决JSON.stringify键顺序不稳定导致的无效触发问题:
方案1:给现有useDidUpdateEffect增加深度比较可选参数(推荐)
仅需修改现有useDidUpdateEffect的逻辑,新增第三个可选参数控制是否开启深度比较,完全兼容原有调用逻辑,无需调整历史代码:
import { useRef, useEffect } from "react"; import { isEqual } from "lodash"; export default function useDidUpdateEffect(effect, deps, deep = false) { const didMount = useRef(false); const prevDeps = useRef(deps); useEffect(() => { if (didMount.current) { let needRunEffect = true; if (deep) { // 开启深度比较时校验依赖是否真实变化 needRunEffect = !prevDeps.current.every((dep, index) => isEqual(dep, deps[index]) ); } if (needRunEffect) { effect(); } } else { didMount.current = true; } // 更新上一次的依赖缓存 prevDeps.current = deps; }, deps); }
调用方式如下,直接传入需要监听的复杂对象,无需做JSON序列化:
useDidUpdateEffect(() => { usersCache.updateCachedUser(data.id, data); }, [data], true);
方案2:组件内手动做深度比较(无需修改公共钩子)
如果不想改动公共的useDidUpdateEffect逻辑,可以直接在Context的业务代码里加一层深度比较判断:
import { isEqual } from "lodash"; export function CurrentUserProvider({ children }) { const [data, setData] = useStateWithCallback(undefined); // 新增ref存储上一次生效的用户数据 const prevUserDataRef = useRef(); const updateData = (data, merge = true, callback = undefined) => { if (merge) { setData((prevData) => ({ ...prevData, ...data }), callback); } else { setData(data, callback); } }; useDidUpdateEffect(() => { // 仅当数据真实变化时才执行缓存同步 if (data?.id && !isEqual(prevUserDataRef.current, data)) { usersCache.updateCachedUser(data.id, data); prevUserDataRef.current = data; } }, [data]); // ... 其余逻辑不变 }
额外优化兜底
可以给usersCache.updateCachedUser增加幂等判断逻辑:写入前先和缓存中对应id的已有数据做深度比较,如果完全一致则直接返回,不执行写入操作,哪怕上游有无效触发也不会产生额外性能开销。
内容的提问来源于stack exchange,提问作者Raul
相关产品推荐
相关产品推荐

