助焊剂存储或行动(或两者)是否应该接触外部服务?

时间:2014-09-02 19:10:24

标签: reactjs reactjs-flux

商店应该保持自己的状态,并且能够在这样做时调用网络和数据存储服务......在这种情况下,操作只是愚蠢的消息传递者,

-OR -

...如果存储是来自操作的不可变数据的愚蠢接收者(并且操作是在外部源之间获取/发送数据的操作?在此实例中的存储将充当视图模型并且将能够聚合/在根据操作提供的不可变数据设置自己的状态之前过滤他们的数据。

在我看来,它应该是一个或另一个(而不是两者的混合)。如果是这样,为什么一个人优先/推荐另一个?

6 个答案:

答案 0 :(得分:150)

我已经看到了两种方式实现了通量模式,并且在完成了两种方式之后(最初使用前一种方法),我认为存储应该是来自操作的数据的愚蠢接收者,并且写入的异步处理应该住在动作创作者中。 (Async reads can be handled differently。)根据我的经验,这有一些好处,按重要性排序:

  1. 您的商店变得完全同步。这使您的商店逻辑更容易遵循并且非常容易测试 - 只是实例化具有某些给定状态的商店,发送操作并检查看看状态是否按预期改变了。此外,流量的核心概念之一是防止级联调度并防止一次多次调度;当您的商店进行异步处理时,这很难做到。

  2. 所有操作都会从操作创建者发送。如果您在商店中处理异步操作,并且希望保持商店的操作处理程序同步(并且您应该为了获得flux single-dispatch guarantee),您的商店将需要触发额外的SUCCESS和FAIL操作以响应异步处理。将这些调度放在动作创建者中有助于分离动作创建者和商店的作业;此外,您不必深入了解商店逻辑,以确定从哪里调度操作。在这种情况下,典型的异步操作可能看起来像这样(根据您使用的通量的风格更改dispatch调用的语法):

    someActionCreator: function(userId) {
      // Dispatch an action now so that stores that want
      // to optimistically update their state can do so.
      dispatch("SOME_ACTION", {userId: userId});
    
      // This example uses promises, but you can use Node-style
      // callbacks or whatever you want for error handling.
      SomeDataAccessLayer.doSomething(userId)
      .then(function(newData) {
        // Stores that optimistically updated may not do anything
        // with a "SUCCESS" action, but you might e.g. stop showing
        // a loading indicator, etc.
        dispatch("SOME_ACTION_SUCCESS", {userId: userId, newData: newData});
      }, function(error) {
        // Stores can roll back by watching for the error case.
        dispatch("SOME_ACTION_FAIL", {userId: userId, error: error});
      });
    }
    

    应该将各种动作之间可能重复的逻辑提取到一个单独的模块中;在这个例子中,该模块将是SomeDataAccessLayer,它处理实际的Ajax请求。

  3. 你需要更少的动作创作者。这不是什么大不了的事,但很高兴。如#2中所述,如果您的商店具有同步操作调度处理(他们应该),您将需要触发额外的操作来处理异步操作的结果。在动作创建器中执行调度意味着单个动作创建者可以通过处理异步数据访问本身的结果来调度所有三种动作类型。

答案 1 :(得分:51)

我在Facebook上向开发人员发送了这个问题,我从比尔费舍尔得到的答案是:

  

在回复用户与用户界面的互动时,我会在动作创建方法中进行异步调用。

     

但是当你有一个自动收报机或其他非人类司机时,来自商店的电话会更好。

     

重要的是在错误/成功回调中创建一个动作,这样数据总是来自动作

答案 2 :(得分:8)

商店应该做所有事情,包括获取数据,以及向商店数据更新的组件发送信号。为什么?因为动作可以是轻量的,一次性的和可更换的,而不会影响重要的行为。所有重要的行为和功能都发生在商店中。这还可以防止在两个非常相似但不同的操作中复制的行为重复。商店是您处理(处理)真相的单一来源。

在每个Flux实现中,我已经看到动作基本上是事件字符串变成了对象,就像传统上你有一个名为"锚点:点击的事件"但在Flux中它将被定义为AnchorActions.Clicked。他们甚至如此"哑巴"大多数实现都有单独的Dispatcher对象来实际将事件分派给正在侦听的商店。

我个人喜欢Reflux' Flux的实现,其中没有单独的Dispatcher对象,Action对象自己进行调度。


编辑:Facebook的Flux实际上吸引了"动作创作者"所以他们确实使用聪明的行动。他们还使用商店准备有效载荷:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/actions/ChatMessageActionCreators.js#L27(第27和28行)

这次完成后的回调将触发一次新操作,并将获取的数据作为有效负载:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51

所以我认为这是更好的解决方案。

答案 3 :(得分:3)

我会提供一个赞成" dumb"动作。

通过将收集视图数据的责任放在您的操作中,您可以将操作与视图的数据要求结合起来。

相比之下,声明性地描述用户的 intent 或应用程序中的某些状态转换的通用Actions允许响应该Action的任何Store将意图转换为特定的状态。对于订阅它的观点。

这适用于更多,但更小,更专业的商店。我认为这种风格是因为

  • 这使您可以更灵活地使用视图消费存储数据
  • "智能"对于复杂的应用程序而言,专门用于消费它们的视图的商店将更小,更少耦合,而不是" smart"可能有许多视图依赖的操作

商店的目的是为视图提供数据。名称"行动"向我建议它的目的是描述我的应用程序的变化。

假设您必须将一个小部件添加到现有的Dashboard视图中,该视图会显示您的后端团队刚刚推出的一些奇特的新聚合数据。

" smart"操作,您可能需要更改您的"刷新 - 仪表板"动作,使用新的API。但是,"刷新仪表板"在抽象意义上没有改变。您的观看次数的数据要求已发生变化。

" dumb"操作,您可以为要使用的新窗口小部件添加新的存储,并将其设置为当它收到"刷新 - 仪表板"操作类型,它发送新数据的请求,并在准备好后将其公开给新的小部件。我有意义的是,当视图层需要更多或不同的数据时,我更改的内容就是数据的来源:商店。

答案 4 :(得分:2)

gaeron的flux-react-router-demo具有很好的“正确”方法的实用变体。

ActionCreator从外部API服务生成承诺,然后将promise和三个操作常量传递给代理/扩展调度程序中的dispatchAsync函数。 dispatchAsync将始终发送第一个操作,例如'GET_EXTERNAL_DATA',一旦承诺返回,它将调度'GET_EXTERNAL_DATA_SUCCESS'或'GET_EXTERNAL_DATA_ERROR'。

答案 5 :(得分:1)

如果您希望有一天的开发环境与您在Bret Victor的着名视频Inventing on Principle中看到的开发环境相当,那么您应该使用愚蠢的商店,这只是对行动/事件的投射。数据结构,没有任何副作用。如果您的商店实际上是同一个全局不可变数据结构的成员,也会有所帮助,例如Redux

此处有更多解释:https://stackoverflow.com/a/31388262/82609