用于乐观更新的操作存储是 Redux/Flux 中的好方法吗?

IT技术 javascript reactjs flux redux
2021-05-03 12:04:13

我一直在 React+Flux 应用程序中使用乐观更新,并看到了两件事:

  1. 如果用户在存在一些未完成的操作时尝试关闭窗口,会发生什么。例如,在 Facebook 中,即使没有真正持久化,消息也会出现在墙上(这就是乐观更新所做的,对用户来说响应更快的应用程序)。但是,如果用户在墙上发帖并立即关闭应用程序(在注销或关闭窗口时),则发帖可能会失败并且他不会收到警报。
  2. 我不喜欢 Stores 管理他自己的实体(例如消息)的想法以及为持久化消息而触发的操作的情况(加载、成功、失败?)。它混合了东西。

所以我致力于此并创建一个ActionStore来管理由组件触发的操作的状态。这是源代码,这是现场演示。

它或多或少是这样工作的:

  1. 组件层次结构的根(redux 中的容器)获取新操作的 nextId 并将其像 props 一样传递给他的孩子(这很丑陋)。
  2. 一个子组件触发一个动作:它保留 actionId 以便在之后向商店询问并调用动作创建者。
  3. 动作创建者创建一个新的动作并向中间件返回一个函数。
  4. 从 Action 返回的函数通过 API 调用创建一个新的 Promise 并分派 XX_START 类型的操作。
  5. ActionStore 监听 XX_START 动作并保存它。
  6. 子组件接收到新状态并找到具有保存 id 的动作并询问它当前的情况:加载、成功或失败。

我这样做主要是为了将“实体”的状态与动作的状态分开,但也允许使用相同的有效载荷重新触发动作(如果服务器暂时关闭或如果我们收到 500 响应状态,这可能很有用用户松动信号)。

此外,拥有动作存储允许在用户注销或关闭窗口之前轻松询问它们是否是待处理的动作。

注意:我正在针对 Rest API 使用单个应用程序页面 Web 应用程序,我不认为在服务器端渲染中使用它

创建 ActionStore 是一个可行的选择,还是我正在破坏一些 Redux/Flux 基础?这会结束使用 React 热重载和时间旅行的可能性吗?

你应该毫不留情地回答,我可能做了很多丑陋的事情,但我正在学习 React/Redux。

1个回答

对于可能有点困惑的未来读者:我们不再称这些函数为“stores”:我们称它们为reducers所以问题是关于reducer 记住动作

我个人不喜欢你在那里建议的方法。您将逻辑付诸行动并为此使用继承。相反,Redux 将操作规定为没有逻辑的普通对象。他们不应该“知道”如何更新某个状态,也不应该持有它——相反,他们应该描述发生了什么。

我认为在 Redux 中实现乐观更新的方法类似于在 Redux 中实现 Undo 的方式,而后者又受到Elm 架构的启发这个想法是你应该编写一个函数,它接受一个减速器(和一些选项)并返回一个减速器:

function optimistic(reducer) {
  return function (state, action) {
    // do something?
    return reducer(state, action);
  }
}

您可以像使用普通减速器一样使用它:

export default combineReducers({
  something: optimistic(something) // wrap "something" reducer
})

现在它只是将状态和动作传递给reducer它包装的对象,但它也可以“记住”动作:

function optimistic(reducer) {
  return function (state = { history: [] }, action) {
    // do something with history?
    return {
      history: [...state.history, action],
      present: reducer(state.history[state.history.length - 1], action)
    };
  }
}

现在它累积了该history领域的所有动作

同样,这本身并不是很有用,但您可能会看到如何实现这样的高阶 reducer:

  • 通过它们的 ID 跟踪挂起的异步操作,并在看到带有 的操作时开始记录历史记录status: 'request'
  • 当它收到带有 的动作时status: 'done',重置历史记录;
  • 当它收到一个动作 with 时status: 'fail',调整历史以不包括初始动作 withstatus: 'request'重新运行减速器以重新计算当前状态,就好像请求从未发生过一样。

当然,我没有提供实现,但这应该让您了解如何以 Redux 方式实现乐观更新。

更新:根据 OP 的评论,redux-optimist似乎正是这样做的。