javascript - React Hooks - useDidUpdateEffect 与深度比较
问题描述
我有以下 useDidUpdateEffect 钩子
import { useRef, useEffect } from "react";
export default function useDidUpdateEffect(effect, deps) {
const didMount = useRef(false);
useEffect(() => {
if (didMount.current) {
effect();
} else {
didMount.current = true;
}
}, deps);
}
我目前使用它如下:
useDidUpdateEffect(() => { ... }, [JSON.stringify(complexNestedObject)]);
问题是,由于我的 dep 复杂而深入,有时会在没有必要的情况下执行回调。
我有一个 useEffect 解决方案,即 useDeepEffect:
import { useRef, useEffect } from "react";
import { isEqual } from "lodash";
export default function useDeepEffect(effect, deps = undefined) {
const isFirst = useRef(true);
const prevDeps = useRef(deps);
useEffect(() => {
const isSame = prevDeps.current.every((obj, index) =>
isEqual(obj, deps[index])
);
if (isFirst.current || !isSame) {
effect();
}
isFirst.current = false;
prevDeps.current = deps;
}, deps);
}
但是我想不出任何可以与 useDidUpdateEffect 进行深入比较的方法,而不必创建另一个钩子。
有任何想法吗?
=====================
更新:
由于这种情况,我处于目前的情况。也许这可以做得更简单,但说实话,我尽力了(我不是超级专业)
- 我有一个包含不同模块的文件夹“api”,其中包含连接到我的服务器的不同功能(这部分我没有使用钩子)
services/
firebase/
api/
users/
helpers/
cache/
usersCache.js
index.js
...
如您所见,我有一个模块“usersCache.js”(记忆模式),它将避免一些 RTT 并降低成本。为了避免 RAM 出现问题,我在那里实现了一个缓存算法“LRU”。
- 在我的应用程序中,我没有使用 REDUX(可能是我采用的最糟糕的想法,但为时已晚,90% 的工作已经完成。将尝试学习这项技术并在生产中采用它,并进行长时间的重构)。
为了管理复杂的状态,我使用了 React Context + useReducer,这在某种程度上简化了我的生活,因为它有点类似于 Redux。
就我而言,对于用户,我有 2 个上下文:
- 当前用户上下文
- 其他用户上下文
它们都包含获取的用户的敏感和非敏感数据。这里你可能会想:为什么要有usersCache?如果两个上下文都可以用作内存缓存?
答案(至少我认为)是:
没有办法在 React 组件或钩子之外使用上下文。为了返回缓存的数据并避免发出服务器请求,不可能在我的 api 模块中执行此操作。
我将敏感数据保存在依赖于当前用户会话的上下文中(例如 isFollowed)。因此,当用户注销时,上下文将被卸载(受保护的路由)。另一方面,我的 usersCache 模块仍然存在,没有敏感数据。
这是我的 CurrentUserContext 的一个示例(因为简单,我在这里没有使用减速器,但是在我的 OtherUsersContext 中,因为状态管理很复杂,所以我这样做了):
import React, { createContext } from "react";
import useDidUpdateEffect from "../../hooks/useDidUpdateEffect";
import useStateWithCallback from "../../hooks/useStateWithCallback";
import { usersCache } from "../../services/firebase/api/users";
const CurrentUserContext = createContext(null);
export default CurrentUserContext;
export function CurrentUserProvider({ children }) {
const [data, setData] = useStateWithCallback(undefined);
const updateData = (data, merge = true, callback = undefined) => {
if (merge) {
setData((prevData) => ({ ...prevData, ...data }), callback);
} else {
setData(data, callback);
}
};
useDidUpdateEffect(() => {
console.log(JSON.stringify(data, null, 2));
/*
As the current user data is not sensitive, we can
synchronize the users cache here.
*/
usersCache.updateCachedUser(data.id, data);
}, [JSON.stringify(data)]); // TODO - Avoid unnecessary executions -> deepCompare ?
return (
<CurrentUserContext.Provider
value={{
data,
setData,
updateData,
}}
>
{children}
</CurrentUserContext.Provider>
);
}
为了避免运行多个 useDidUpdateEffects,我正在对用户数据进行字符串化。但是,由于它复杂且嵌套:
userData = {
avatar: {
uri,
thumbnailUri,
},
...
}
在数据没有变化的情况下执行效果,因为接收到相同的数据是无序的:
userData = {
avatar: {
thumbnailUri,
uri,
},
...
}
顶级字段也不是无序的。
解决方案
我认为(我可能是错的)如果你使用这个效果,整个问题就会消失:
useEffect(() => {
if(!data) return;
// do some stuff
},[data]);
推荐阅读
- android - 谷歌地图中自定义集群图标的问题
- python - python - 在 3d 游戏中移动鼠标不起作用
- google-cloud-platform - 我的应用程序的验证状态显示为谷歌 OAuth 同意屏幕中所需的开发人员操作
- javascript - 在显示中获取 [object HTMLInputElement] 而不是数字
- google-cloud-platform - (GRPC 转码)REST API 上的 502 服务器错误和 GRPC 上的 200 OK - GKE 上的云端点
- perl - 在 perl 中将 Outlook htmlrtf 转换为 html
- r - 如何创建一个闪亮的应用程序,其中选项卡仅在满足条件时才可见
- python - django 如何随机合并两个不同的查询集(但有一些共同的字段)
- jdbc - 如何使用 camel-quarkus-jdbc 定义路由?
- sql - 是否有一个 Oracle SQL 函数可以返回一行,其中的列基于一列中不同的不同值?