商店应该保持自己的状态,并具有调用网络和数据存储服务的能力,在这种情况下,操作只是简单的消息传递者。
或者
商店是否应该成为不可变数据的哑收件人(并且操作应该在外部源之间获取/发送数据)?在这种情况下,Store将充当视图模型,并能够在根据动作提供的不可变数据设置其自身状态之前聚合/过滤其数据。
对我来说,似乎应该选择一种方式(而不是两者混合)。如果是这样,为什么一个方法比另一个更受推荐?
商店应该保持自己的状态,并具有调用网络和数据存储服务的能力,在这种情况下,操作只是简单的消息传递者。
或者
商店是否应该成为不可变数据的哑收件人(并且操作应该在外部源之间获取/发送数据)?在这种情况下,Store将充当视图模型,并能够在根据动作提供的不可变数据设置其自身状态之前聚合/过滤其数据。
对我来说,似乎应该选择一种方式(而不是两者混合)。如果是这样,为什么一个方法比另一个更受推荐?
我曾经看到通量模式有两种实现方式,在自己尝试了这两种方法后(最初采用前一种方法),我认为存储应该是来自操作的数据的愚笨接收者,并且写入的异步处理应该在操作创建者中进行。(异步读取可以有不同的处理方式)。根据我的经验,这样做有一些好处,按重要性排序如下:
您的存储变得完全同步。 这使得您的存储逻辑更易于跟踪,并且非常容易进行测试 - 只需使用给定状态实例化一个存储,发送一个操作,然后检查状态是否按预期更改即可。此外,在通量中的核心概念之一是防止级联分派和同时分派多个操作;如果您的存储执行异步处理,则很难实现这一点。
所有操作分派均来自操作创建者。 如果您在存储中处理异步操作,并且要保持存储的操作处理程序同步(为了获取通量单个分派保证),则存储需要在响应异步处理时触发额外的“成功”和“失败”操作。将这些分派放在操作创建者中可以帮助分离操作创建者和存储的作业;此外,您无需深入挖掘存储逻辑以找出从哪里分派操作。在这种情况下,典型的异步操作可能如下所示(根据您使用的通量类型更改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请求。
您需要更少的操作创建者。 这不是很重要,但很好有。如#2所述,如果您的存储具有同步操作分发处理(并且应该),则需要触发额外的操作来处理异步操作的结果。在操作创建者中执行分发意味着一个单一的操作创建者可以通过处理异步数据访问的结果来分派所有三种操作类型。
"SOME_ACTION"
),使用 API 发出请求(SomeDataAccessLayer.doSomething(userId)
)返回一个 Promise,在两个 .then
函数中,分派其他操作。如果应用程序需要了解状态的状态,则请求状态可以(或多或少)映射到存储状态。如何映射取决于应用程序(例如,也许每个评论都有单独的错误状态,就像 Facebook 一样,或者可能有一个全局错误组件)。 - Michelle Tilley我在Twitter上向Facebook的开发人员提问,Bill Fisher给我的答复是:
当响应用户与UI交互时,我会在操作创建器方法中进行异步调用。
但是当你有一个ticker或其他非人驱动程序时,从store调用更好。
重要的是在错误/成功回调中创建一个action,以便数据始终起源于action。
商店应该做所有事情,包括获取数据,并向组件发出信号,表明商店的数据已更新。 为什么?因为这样可以使操作变得轻量级、可丢弃和可替换,而不影响重要行为。所有重要的行为和功能都发生在商店中。这也防止了行为的重复,在两个非常相似但不同的操作中会复制行为。商店是您的唯一的(处理)真相来源。
在我看到的每个Flux实现中,Actions基本上是将事件字符串转换为对象,就像传统上你会有一个名为“anchor:clicked”的事件,但在Flux中它将被定义为AnchorActions.Clicked。它们甚至如此“愚蠢”,以至于大多数实现都有单独的Dispatcher对象来实际将事件分派到正在侦听的商店。
个人喜欢Reflux对Flux的实现方式,其中没有单独的Dispatcher对象,而Action对象自己进行分派。
编辑:Facebook的Flux实际上会获取“action creators”,因此他们确实使用智能操作。他们还使用存储器准备有效负载:
完成后的回调将触发新的操作,这次使用获取的数据作为有效负载:
所以我想那应该是更好的解决方案。
我将支持“愚蠢”的 Actions。
通过在 Actions 中添加收集视图数据的职责,您将使您的 Actions 与视图的数据要求联系在一起。
相比之下,泛用的 Actions 只是声明用户意图或应用程序中的某些状态转换,允许任何响应该 Action 的 Store 将意图转换为专门针对订阅它的视图定制的状态。
这适用于更多但更小、更专业化的 Stores。我支持这种风格,因为
存储器的目的是向视图提供数据。“Action”这个名字让我觉得它的目的是描述我的应用程序中的变化。
假设您需要向现有的仪表板视图添加一个小部件,该小部件显示后端组新推出的一些花哨的聚合数据。
使用“智能” Actions,您可能需要更改“refresh-dashboard” Action,以便使用新 API。但是,“抽象意义上的刷新仪表板”并没有改变。您的视图数据要求已经发生了变化。
使用“愚蠢”的 Actions,您可以为新小部件添加一个新的 Store,并设置它,以便在接收到“refresh-dashboard” Action 类型时,发送请求获取新数据,并在准备就绪后将其显示给新小部件。我认为当视图层需要更多或不同的数据时,我更改的是那些数据源:Stores。
gaeron的flux-react-router-demo有一种不错的实用方法。
ActionCreator从外部API服务生成一个Promise,然后将该Promise和三个Action常量传递给代理/扩展Dispatcher中的dispatchAsync
函数。dispatchAsync
将始终分派第一个Action,例如“GET_EXTERNAL_DATA”,一旦Promise返回,它将分派“GET_EXTERNAL_DATA_SUCCESS”或“GET_EXTERNAL_DATA_ERROR”之一。