【问题标题】:React - Caching data inside reducers?React - 在减速器中缓存数据?
【发布时间】:2021-11-08 06:42:00
【问题描述】:

总结

在 reducer 中更新缓存好不好?我的意思是,是否可以在我的前端的每个部分(组件、屏幕、挂钩、帮助程序...)中更新缓存,还是应该将此行为委托给系统的特定部分?

简介

在我的应用中,每个用户都有一个字段“totalFollowers”。

我将每个用户的数据存储在 React Context 的状态中。为了根据之前的状态执行复杂而纯粹的更新,我使用了 reducer。

此外,我还实现了内存中的 LRU 缓存,只是为了避免不必要的数据库请求并提高用户体验/性能。在这个缓存中,我存储了一些数据,这些数据也包含在我谈到的 React 上下文中......所以,在双方,数据必须相等。

当当前用户关注他时,我正在更新特定用户的 totalFollowers 字段,在我的减速器中,我有以下内容:

export default (otherUsers, action) => {   
    switch (action.type) {
       case "follow-user": {
          const { userId, isFollowing } = action;

          const prevUserData = otherUsers.get(userId);

          return new Map([
             ...otherUsers,
            [
                userId,
                {
                   ...prevUserData,
                   totalFollowers: prevUserData.totalFollowers + (isFollowing ? 1 : -1)
                 ]
            ]
         );
       }

       ...
    }
}

问题

正如我之前所说...我的缓存有一些数据也包含在上下文中(使用我描述的 reducer),“totalFollowers”就是其中之一。

如果我的 reducer 让我能够以本机/原子方式访问状态,我认为在其中更新我的缓存是个好主意。但是,我读过减速器的唯一目的是更新状态......我很害怕,因为我更新减速器内部缓存的想法可能是一种反模式(避免纯函数内部的副作用)

你怎么看?在 reducer 中执行缓存更新是否“正确”?

import { usersCache } from "../../services/firebase/api/users"

export default (otherUsers, action) => {   
    switch (action.type) {
       case "follow-user": {
          const { userId, isFollowing } = action;

          const prevUserData = otherUsers.get(userId);

          const newTotalFollowers = prevUserData.totalFollowers + (isFollowing ? 1 : -1);

          usersCache.updateUser(userId, { totalFollowers: newTotalFollowers }); // merge update

          return new Map([
             ...otherUsers,
            [
                userId,
                {
                   ...prevUserData,
                   totalFollowers: newTotalFollowers
                 ]
            ]
         );
       }

       ...
    }
}

【问题讨论】:

  • 我认为你想多了。 React 状态基本上已经是内存中的缓存。我不认为将数据缓存在内存中已经存在的内存中会给您带来任何好处。您会看到缓存/记忆化的好处在于您的数据库/网络请求进入您的状态,以及派生状态的记忆出来 你的状态到 UI。我建议查看Redux 进行状态管理,并查看Reselect 来记住您选择的状态。 Redux-toolkit 为方便起见重新导出重新选择,顺便说一句。
  • @DrewReese 认为当组件卸载或用户会话关闭时状态被破坏(我的导航中的身份验证保护路由)。我正在使用该缓存来避免发出数据库请求,而不是在 GUI 中呈现内容,只是为了返回缓存的数据。 redux 能解决这个问题吗?
  • 我的意思是,IMO 您正在尝试优化代码的错误部分。是的,Redux 是一个全局状态管理工具,使用 reducer,所以如果你已经在使用 useReducer 钩子会很熟悉,它是使用 React 上下文 API 构建的,并且可以很好地扩展。 Redux-toolkit 是对 redux 和 react-redux 的抽象,包括缓存对后端系统的查询 (redux-toolkit.js.org/rtk-query/overview)。
  • @DrewReese 非常感谢您的帮助,我会尽快迁移到 Redux。但是,为什么您认为不需要缓存用户数据?在我的用例中,我只是保存最新的数据(可以来自用户配置文件中的下拉刷新,并且可以在按下典型的关注按钮时进行修改(totalFollowers 字段))。我以前认为我的上下文可能是“缓存”,但是不可能在我的 api 模块中使用 React 上下文......并且当用户退出时它将被卸载,我认为记忆模式可能很好.
  • 在上下文中同步路由。在缓存中也有记忆结果然后上下文被卸载(用户退出)。

标签: javascript reactjs react-native caching


【解决方案1】:

缓存和reducer是两个不同的东西。

减速器

只有当你想构建一个迷你状态机时,你才会选择使用 reducer。但是即使这样做也有其自身的问题,通常对状态的任何属性的每次更新都应该触发更新。因此,reducer 与否主要取决于您正在构建的逻辑是否真正压缩(或扭曲)在一组相关属性中。

缓存

我有一种直觉,你比我更了解缓存。所以我会跳过这部分。您使用缓存来确保在满足相同条件时可以快速返回内容,并且您还想确保至少有两个以上的条件。不然就浪费了。

上下文

您也提到了上下文,但上下文也不同。它确保您可以共享价值。但棘手的部分是,每当上下文值因状态变化而发生变化时,它都会渲染其下的所有子项。

两美分

通常你会认为缓存是对reducer(或上下文)很好的补充。但是这里的好处是缓存,例如它可以节省跳过计算的时间。

但不要赌渲染。即使从内存中获取,连接到 reducer 或上下文的值仍然会改变!这意味着您仍然可以通过缓存获得 100% 的重新渲染。

我想说的是,缓存是一个与 React 无关的概念。如果你应用它,你只会得到它自己的好处,而不要期望其他好处。

【讨论】:

  • 他可以两全其美!使用缓存来降低网络成本并避免 RTT 是好的。但是,正如您所说,更新状态总是会导致重新渲染。如果他有两种方法来更新它的上下文状态呢?由于它是一个 UsersContext,他可以完美地提供一个 updateUser 方法,该方法将简单地更新状态(通常用于 pull-to-refresh,其中需要重新渲染),以及一个 addUser 方法,在 reducer 中,在更新状态之前会检查地图中是否存在记录(如果存在,返回相同的状态,否则返回更新的状态)
  • 另外,在更新状态之前,可以通过updateUser方法进行深度比较,如果之前的记录相等,则返回当前状态,而不是更新后的状态。
  • 顺便说一句,我并不是很喜欢 reducer……也许有条件地更新状态或在 reducer 中运行 lodash 深度比较是一种反模式。
  • @Victor,你是对的。您可以同时利用这两个优势,但我的观点是,每个优势都是独一无二的,没有我们通常认为的综合优势。老实说,在某些方面,它们相互矛盾。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 2018-11-24
  • 2020-06-09
  • 1970-01-01
  • 2016-11-23
相关资源
最近更新 更多