商店应该保持自己的状态并能够调用网络和数据存储服务……在这种情况下,这些动作只是愚蠢的消息传递者,
-要么-
...商店应该是动作中不可变数据的愚蠢接收者(而动作是在外部源之间获取/发送数据的动作吗?在这种情况下,商店将充当视图模型,并且能够聚合/过滤它们的数据根据动作所馈送的不可变数据设置自己的状态之前。
在我看来,它应该是一个或另一个(而不是两者的结合)。如果是这样,为什么一个偏爱/推荐另一个偏爱?
商店应该保持自己的状态并能够调用网络和数据存储服务……在这种情况下,这些动作只是愚蠢的消息传递者,
-要么-
...商店应该是动作中不可变数据的愚蠢接收者(而动作是在外部源之间获取/发送数据的动作吗?在这种情况下,商店将充当视图模型,并且能够聚合/过滤它们的数据根据动作所馈送的不可变数据设置自己的状态之前。
在我看来,它应该是一个或另一个(而不是两者的结合)。如果是这样,为什么一个偏爱/推荐另一个偏爱?
Answers:
我已经看到两种方式都实现了通量模式,并且我自己都做完之后(最初采用了前一种方法),我认为存储应该是动作中数据的愚蠢接收者,并且写操作的异步处理应该存在于动作创作者。(异步读取的处理方式可能有所不同。)以我的经验,按重要性顺序,它有一些好处:
您的商店将完全同步。这使您的商店逻辑更易于遵循和测试,非常容易-只需使用某个给定状态实例化商店,向其发送操作,然后检查状态是否按预期更改。此外,流量中的核心概念之一是防止级联调度并同时防止多个调度。当商店进行异步处理时,这很难做到。
所有动作分派均由动作创建者执行。如果您在商店中处理异步操作,并且希望使商店的动作处理程序保持同步(并且应该为了获得通量单次派发保证),则商店将需要触发其他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请求。
您需要较少的动作创建者。这没什么大不了的,但是很高兴。如#2所述,如果您的商店具有同步操作分派处理(应该这样做),则需要触发额外的操作来处理异步操作的结果。在动作创建者中进行分派意味着单个动作创建者可以通过处理异步数据访问本身的结果来分派所有三种动作类型。
"SOME_ACTION"
),使用API发出一个SomeDataAccessLayer.doSomething(userId)
返回诺言的请求(),并在这两个.then
函数中调度其他操作。如果应用程序需要知道状态的状态,则请求状态可以(或多或少)映射到存储状态。此地图的方式取决于应用程序(例如,每个评论都有一个单独的错误状态,一个la脸书,或者可能有一个全局错误组件)
我在Facebook上向开发人员发布了这个问题,我从比尔·费舍尔那里得到的答案是:
当响应用户与UI的交互时,我将在动作创建者方法中进行异步调用。
但是,如果您有股票报价器或其他非人工驾驶员,则从商店拨打电话会更好。
重要的是在错误/成功回调中创建操作,因此数据始终源自操作
a call from store works better when action triggers from non-human driver
呢?
商店应该做所有事情,包括获取数据,并向组件发出信号,通知商店的数据已更新。为什么?因为动作可以是轻量级的,一次性的和可替换的,而不影响重要的行为。所有重要的行为和功能都在商店中发生。这也防止了行为的重复,否则这些行为将以两个非常相似但不同的动作来复制。商店是您(处理)事实的唯一来源。
在每个Flux实现中,我都看到Actions基本上是将事件字符串转换为对象,就像传统上一样,您有一个名为“ anchor:clicked”的事件,但是在Flux中,它将定义为AnchorActions.Clicked。它们甚至太“笨拙”,以致于大多数实现都有单独的Dispatcher对象,以将事件实际分派到正在监听的商店。
我个人很喜欢Reflux的Flux实现,其中没有单独的Dispatcher对象,而Action对象自己进行调度。
编辑:Facebook的Flux实际上会获取“动作创建者”,因此他们确实使用了智能动作。他们还使用存储来准备有效负载:
然后,完成时的回调将这次以提取的数据作为有效负载触发新动作:
因此,我认为这是更好的解决方案。
我将提供一个支持“哑”动作的论点。
通过将收集视图数据的责任置于操作中,您可以将操作与视图的数据需求结合起来。
相反,以声明方式描述用户意图或应用程序中的某些状态转换的通用动作,允许任何响应该动作的商店将其转换为专门针对所预订视图的状态。
这使其适合更多但更小,更专业的商店。我主张这种风格,因为
存储的目的是向视图提供数据。“动作”这个名称向我暗示,其目的是描述我的应用程序中的更改。
假设您必须将一个小部件添加到现有的“仪表板”视图中,该视图显示您的后端团队刚刚推出的一些新的汇总数据。
使用“智能”操作,您可能需要更改“刷新仪表板”操作,以使用新的API。但是,抽象意义上的“刷新仪表板”没有改变。视图的数据要求已发生变化。
使用“哑”动作,您可以添加一个新的商店以供新的小部件使用,并进行设置,以便在收到“刷新仪表板”动作类型时发送对新数据的请求,并将其公开给新的小部件准备就绪后。对我来说,当视图层需要更多或不同的数据时,我所更改的就是这些数据的来源:存储。
gaeron的flux-react-router-demo具有“正确”方法的一个很好的实用程序变体。
一个ActionCreator从一个外部API服务生成一个Promise,然后将Promise和三个动作常量传递dispatchAsync
给代理/扩展Dispatcher中的一个函数。dispatchAsync
将始终调度第一个操作,例如“ GET_EXTERNAL_DATA”,并且一旦诺言返回,它将调度“ GET_EXTERNAL_DATA_SUCCESS”或“ GET_EXTERNAL_DATA_ERROR”。
如果您希望有一天有一个与Bret Victor著名的视频Inventing on Principle所看到的开发环境相当的开发环境,则应该使用哑存储,它们只是数据结构内动作/事件的投影,而没有任何副作用。如果您的商店实际上是相同的全局不可变数据结构的成员(例如在Redux中),这也将有所帮助。