Redux 状态在窗口重新加载时重置(客户端)

Posted

技术标签:

【中文标题】Redux 状态在窗口重新加载时重置(客户端)【英文标题】:Redux State Resets On Window Reload (Client Side) 【发布时间】:2018-06-07 12:50:46 【问题描述】:

我有非常大且复杂的对象,例如 userInfo、chatInfo 等,例如在对象和数组中具有非常大且嵌套的信息。每次刷新页面时,我的反应应用程序都会重置 redux 状态,我必须再次调用所有这些 API。

我对这个话题做了一些研究。我查看了 Dan Abramov 的关于 redux 的蛋头教程。他所做的是在浏览器的 localStorage 中维护 redux 状态,并在每 100 或 500 毫秒后更新 localStorage。我觉得这是一种代码味道。

不断观察localStorage状态并更新它,不会影响浏览器的性能。我的意思是这不是 Angular 1 失败的原因,因为它不断地观察状态变量,过了一段时间,如果网站在浏览器中保持活动状态,它就会变慢。因为我们的脚本不断地检查变量的状态。我觉得好像我们在这里做同样的事情。

如果在 localStorage 中维护 redux 状态是正确的方法,有人可以告诉我为什么会这样吗?如果没有,还有更好的方法吗?


这不是 How can I persist redux state tree on refresh? 的重复,因为我在寻求建议,在本地存储中持久化状态是否是代码异味

【问题讨论】:

我认为在 localStorage 中存储是正确的方法。我认为你不看localStorage,你只是更新它。假设您有 20 个变量要存储在 localStorage 中,只有在页面中这些变量发生更新时才更新 localStorage。 对我来说,刷新运行 SPA 的浏览器相当于重新启动计算机上的程序。我认为在大多数情况下,清除状态并在刷新时再次调用 API 是一件好事。如果页面进入奇怪的状态,您的用户将如何重置您的页面?正如您所建议的那样,长时间管理全局状态在我看来风险很大,应该避免。 redux-persist 是事情 How can I persist redux state tree on refresh?的可能重复 这不是 ***.com/questions/37195590/… 的副本,因为我在寻求建议,在本地存储中持久化状态是否是代码异味 @michael-freidgeim 【参考方案1】:

我认为在这里使用localStorage 是您的最佳选择,因为客户端似乎需要您存储在那里的数据。如果数据没有变化,您不需要重复查询或观察localStorage

您可以做的另一件事是将closure 包裹在您的localStorage 周围,这样您在检索“大”数据时就不会总是敲击磁盘。每个浏览器都以不同的方式实现localStorage,因此无法保证一致的行为或 I/O 性能。

这还添加了一个简单的抽象层,它隐藏了实现,并在一个地方控制与您的用户数据相关的所有内容。

这里是一个简单的用户配置文件数据关闭示例:

// UserProfile.js
var UserProfile = (function() 
  var userData = ;

  var getUserData = function() 
    if (!userData)  
      userData = JSON.parse(localStorage.getItem("userData"));
    
    return userData;
  ;

  var setUserData = function(userData) 
    localStorage.setItem("userData", JSON.stringify(userData));
    userData = JSON.parse(localStorage.getItem("userData"));
  ;

  return 
    getUserData: getUserData,
    setUserData: setUserData
  
)();

export default UserProfile;

设置用户数据对象:这将覆盖localStorage,并在闭包内设置局部变量。

import UserProfile from '/UserProfile';
UserProfile.setUserData(newUserData);

获取用户数据对象:这将从闭包内的局部变量中获取数据,如果未设置则从localStorage获取。

import UserProfile from '/UserProfile';
var userData = UserProfile.getUserData();

这里的想法是将数据从localStorage 加载到内存中,第一次,当您的应用加载时,或在第一次 API 调用时。直到用户个人资料数据发生变化(例如,用户更新他们的个人资料),您将再次查询 API,并通过 UserProfile.setUserData(..) 调用再次更新数据。

【讨论】:

【参考方案2】:

问题是你需要在什么时候实现持久性。我觉得您的情况的答案是重新加载页面。因此,如果您担心性能,我会说: * 仅在状态更改时更新 localStorage。更新状态时在减速器内部。 * 启动应用时从 localStorage 读取。

(这样你只在状态改变时才写,而且你只读一次)

附: 我会推荐 https://github.com/rt2zz/redux-persist 包以在 Redux 应用程序中实现持久化。

【讨论】:

我同意,redux-persist 是我认为的答案【参考方案3】:

我只会将其作为弱缓存来执行,并且不会依赖它。本地存储空间有限(例如 Chrome 上的 5mb),并且可能不可用。您必须仔细验证您的数据是否已写入。

正如其他人所说,您不会观看 localStorage,而是会定期刷新商店。但我同意,盲目地假设您的所有状态都适合保存在本地存储中,这似乎是一种相当粗略的技巧。与所有缓存解决方案一样,您需要仔细考虑其含义(新鲜度、过期等)。听起来您可能希望逐步执行此操作 - 挑选一些适合缓存的低挂水果,并考虑在本地存储中缓存该状态的含义。

【讨论】:

【参考方案4】:

尝试 redux-persis你可以做乐观的持久化,与底层平台(网络/移动)无关。

如果您仍然发现性能是一个瓶颈。您可以分步执行以下任一操作。

缓存中间件

创建一个中间件,监听变化并记录它们,但仅每 5 秒刷新一次。

将事件处理程序附加到 window.beforeunload 以检测用户离开或关闭浏览器以刷新更改

持久性策略

要持久化数据,您可以使用以下两种策略之一。

如果没有性能瓶颈,请将其存储在 localStorage 中。 像文件上传一样向服务器发送 JSON Blob。获取应用加载时的最后一个 JSON 状态并在本地保存。

我建议你从使用 redux-persist 开始。如果仍然存在性能瓶颈,请使用缓存中间件以及两种持久性策略之一。

【讨论】:

【参考方案5】:

我认为在大多数情况下,您希望在某些操作发生后将您的状态保持在localStorage。至少在我需要坚持的项目中,情况总是如此。

因此,如果您使用 redux-sagaredux-observableredux-cycles 来协调您的副作用,那么您可以轻松地使副作用(将状态持久化到 localStorage)在其中一个操作发生时发生地方。我认为这比基于时间间隔“随机”持久化数据要好得多。

【讨论】:

【参考方案6】:

您对监视状态的理解似乎是您有某种间隔,它将不断检查您的状态并在 localStorage 中更新它。但是,我认为您可以通过在反应生命周期方法componentDidUpdate 中更新您的 localStorage 来实现同样的目的。此方法将在您的组件每次更新时触发,因此您可以利用它并在每次触发时更新您的 localStorage,而不会导致任何性能损失。

【讨论】:

【参考方案7】:

一种选择是加载 INITIAL_STATE 中的数据

window.__INITIAL_STATE__ =  ...state... 

并加载减速器:

window.__APP_STATE__.__REDUCERS__.push(reducer) 

【讨论】:

【参考方案8】:

在大型 redux+react 应用程序中持久化状态是完全可以的。

关于 angular1、观察者和摘要循环,即使您 rehydrate 指出,由于 redux 连接 API 和反应的 vrtualDOM,并非所有组件都被渲染。

您可以查看:

https://github.com/redux-offline/redux-offline https://github.com/rt2zz/redux-persist

性能不应该是您最关心的问题。如果涉及到使您的状态正常化,并且只保留草稿等重要信息(更少的信息 - 更少的补液)。

这种设置的更大问题通常是应用程序中有任何后台同步或套接字更新。拥有多个浏览器选项卡会导致异步写入本地数据库,从而导致覆盖以前的状态。

这是一个用于交叉表同步的薄包装redux-persist-crosstab

您可以在 mattermost webapp 中查看其中一些实现以及它们如何将其用于实时应用程序。

为了额外的稳定性和性能,他们经历了一些困难 - link to store file in mattermost webapp

【讨论】:

【参考方案9】:

我认为你可以使用名为 redux-storage 的 npm 模块。它为具有灵活后端的 redux 提供持久层。你可以将 redux-storage 与任何你想要的 redux-storage-engine 结合起来。在我看来,您可以将其与redux-storage-engine-sessionstorage 结合使用,假设您只想在浏览器打开之前共享和保存信息。无需使用 localStorage 膨胀浏览器。您需要额外的 javascript 支持才能清楚地说明这一点。

Redux-storage 将在每次状态更改后触发加载和保存操作。在加载和保存的情况下提供更多的灵活性。此外,如果您不保存某些状态更改,那么您可以定义要过滤掉的状态数组。

【讨论】:

redux-storage 的新位置是github.com/react-stack/redux-storage

以上是关于Redux 状态在窗口重新加载时重置(客户端)的主要内容,如果未能解决你的问题,请参考以下文章

Redux 商店使用 nextJS 自行重置路由更改

使用 Apollo 客户端时在 Redux 中调度操作会导致 Apollo 客户端重置查询

Redux dispatch 导致组件本地状态重置

更新 redux 状态/提交表单时页面重新加载

React / Redux 加载具有副作用的数据

在使用 redux-persist 重新水化存储之前加载初始状态的默认值