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