【问题标题】:A Store of Actions for optimistic updates is a good approach in Redux/Flux?用于乐观更新的操作存储是 Redux/Flux 中的好方法吗?
【发布时间】:2015-10-31 11:30:30
【问题描述】:

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

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

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

它或多或少像这样工作:

  1. 组件层次结构的根(redux 中的容器)获取新操作的 nextId 并将其像道具一样传递给他的孩子(这很难看)。
  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。

【问题讨论】:

    标签: javascript reactjs flux redux


    【解决方案1】:

    对于可能有点困惑的未来读者:我们不再称这些函数为“商店”:我们称它们为reducers。所以问题是关于a reducer remembering actions

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

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

    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' 的操作时,重置历史记录;
    • 当它接收到带有status: 'fail' 的操作时,调整历史以不包括带有status: 'request' 的初始操作,并且重新运行reducer 以重新计算当前状态,就好像请求从未发生过一样。

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

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

    【讨论】:

    • 感谢丹的帮助。我不明白您为什么说 reducer 必须重新计算当前状态,就好像请求从未发生过一样。如果我有一个通过加载旋转触发操作的 React 组件,我想在操作失败时显示渲染错误消息。如果我失去行动将如何实现?实际上我正在使用基于redux-promise 的中间件,我认为这是我想要实现的一个好方法,不是吗?
    • “我不明白你为什么说reducer必须重新计算当前状态,就好像请求从未发生过一样。”我认为问题是关于乐观的更新。使用乐观更新,如果失败,您希望回滚您在请求开始时所做的任何突变。显示错误消息与乐观更新无关;有关示例,请参见 stackoverflow.com/questions/31822706/…
    • 你说得对,我有点混淆了这些概念。我将考虑您的建议并尝试实施。我也会看看redux-optimist,也许这就是我想要的。一个简单的观点,我认为如果我们想要实现响应性,显示错误消息与乐观更新密切相关,ToDo 项不能简单地从屏幕上弹出。
    • 是的,redux-optimist 看起来和我想要描述的完全一样。
    猜你喜欢
    • 2015-03-15
    • 2018-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-19
    • 1970-01-01
    • 1970-01-01
    • 2015-09-21
    相关资源
    最近更新 更多