Flux 中的 AJAX:依赖状态更改时刷新存储
Posted
技术标签:
【中文标题】Flux 中的 AJAX:依赖状态更改时刷新存储【英文标题】:AJAX in Flux: Refreshing stores when dependent state changes 【发布时间】:2015-03-31 05:13:20 【问题描述】:我正在使用 MartyJS 构建一个 Flux 应用程序(它非常接近“香草”Flux 并使用相同的底层调度程序)。它包含具有固有依赖关系的商店。例如,UserStore
跟踪当前用户,InstanceStore
跟踪当前用户拥有的数据实例。实例数据是从 API 异步获取的。
问题是当用户更改时,InstanceStore
的状态如何处理。
我开始相信(例如阅读@fisherwebdev 在 SO 上的答案),在动作创建函数中发出 AJAX 请求是最合适的,并且让 AJAX “成功”导致动作反过来导致存储改变。
因此,为了获取用户(即登录),我在动作创建器函数中进行了 AJAX 调用,当它解析时,我将用户作为有效负载发送 RECEIVE_USER
动作。 UserStore
监听并相应地更新其状态。
但是,如果用户更改,我还需要重新获取InstanceStore
中的所有数据。
选项1:我可以在InstanceStore
中监听RECEIVE_USER
,如果是新用户,触发AJAX请求,进而创建另一个动作,进而导致InstanceStore
进行更新。这样做的问题是它感觉像是级联动作,虽然从技术上讲它是异步的,所以调度程序可能会允许它。
选项 2:另一种方法是让 InstanceStore
监听 UserStore
发出的更改事件,然后进行请求-动作舞蹈,但这也感觉不对。
选项 3:第三种方法是让动作创建者协调两个 AJAX 调用并分别调度这两个动作。但是,现在动作创建者必须非常了解商店之间的关系。
Where should ajax request be made in Flux app? 中的一个答案让我认为选项 1 是正确的,但 Flux 文档也暗示存储触发操作不好。
【问题讨论】:
【参考方案1】:我通常使用 Promise 来解决这种情况。 例如:
// UserAction.js
var Marty = require( 'marty' );
var Constants = require( '../constants/UserConstants' );
var vow = require( 'vow' );
module.exports = Marty.createActionCreators(
...
handleFormEvent: function ( path, e )
var dfd = vow.defer();
var prom = dfd.promise();
this.dispatch( Constants.CHANGE_USER, dfd, prom );
);
// UserStore.js
var Marty = require( 'marty' );
var Constants = require( '../constants/UserConstants' );
module.exports = Marty.createStore(
id: 'UserStore',
handlers:
changeUser : UserConstants.CHANGE_USER
,
changeUser: function ( dfd, __ )
$.ajax( /* fetch new user */ )
.then(function ( resp )
/* do what you need */
dfd.resolve( resp );
);
);
// InstanceStore.js
var Marty = require( 'marty' );
var UserConstants = require( '../constants/UserConstants' );
module.exports = Marty.createStore(
id: 'InstanceStore',
handlers:
changeInstanceByUser : UserConstants.CHANGE_USER
,
changeInstanceByUser: function ( __, prom )
prom.then(function ( userData )
/* OK, user now is switched */
$.ajax( /* fetch new instance */ )
.then(function ( resp ) ... );
);
【讨论】:
【参考方案2】:选项 3 对我来说似乎是最干净的解决方案,其次是选项 1。我的推理:
选项 2 偏离了处理商店之间依赖关系的预期方式(等待),您必须在每个更改事件之后检查以确定哪些是相关的,哪些可以忽略,或者开始使用多种事件类型;它可能会变得非常混乱。
我认为选项 1 是可行的;正如您链接的帖子中的Bill Fisher remarked 一样,只要通过调用新操作来处理结果数据,就可以从商店内部进行 API 调用。但 OK 并不一定意味着理想,如果您可以将所有 API 调用和动作启动收集在一个地方(即 ActionCreators),您可能会实现更好的关注点分离并减少级联。这与选项 3 一致。
但是,现在动作创建者必须对商店的运作方式有很多了解 相互关联。
在我看来,动作创建者不需要知道商店在做什么。它只需要登录一个用户,然后获取与该用户关联的数据。无论这是通过一个或两个 API 调用来完成的,它们在逻辑上非常紧密地耦合,并且在一个动作创建者的范围内是有意义的。用户登录并获取数据后,您可以触发两个操作(例如 LOGGED_IN、GOT_USER_DATA),甚至可以只触发一个包含两者所需的所有数据的操作。无论哪种方式,这些操作都只是在呼应 API 调用所做的事情,由商店决定如何处理它。
我建议使用单个操作来更新两个存储,因为这似乎是waitfor
的完美用例:当一个操作触发两个存储中的处理程序时,您可以指示 InstanceStore 等待 UserStore 的处理程序完成在 InstanceStore 的处理程序执行之前。它看起来像这样:
UserStore.dispatchToken = AppDispatcher.register(function(payload)
switch (payload.actionType)
case Constants.GOT_USER_DATA:
...(handle UserStore response)...
break;
...
);
...
InstanceStore.dispatchToken = AppDispatcher.register(function(payload)
switch (payload.actionType)
case Constants.GOT_USER_DATA:
AppDispatcher.waitFor([UserStore.dispatchToken]);
...(handle InstanceStore response)...
break;
...
);
【讨论】:
【参考方案3】:选项 1 在概念上对我来说似乎是最佳选择。有 2 个单独的 API 调用,因此您有 2 组事件。
这是在少量代码中的大量事件,但 Flux 始终依赖于使用简单、标准的 Action->Store->View 方法。一旦你做了一些聪明的事情(比如选项 2),你就改变了它。如果其他开发人员不能再安全地假设任何 Action 流程都与其他所有流程完全相同,那么您就失去了 Flux 的一大优势。
但它不会是代码中最短的方法。 MartyJS 看起来至少会比 Facebook 自己的 Flux 库整洁一点!
另一种选择;如果登录必须始终刷新 InstanceStore,为什么不让登录 API 调用也包含所有 InstanceStore 数据?
(更进一步;为什么有 2 个独立的商店?无论哪种方式,它们似乎都非常耦合,而且没有理由您仍然不能在不重新调用登录的情况下调用 InstanceStore API)
【讨论】:
两个 store 的原因是所有数据都以某种方式与用户相关联(数据基本上是一个图表),所以你要么在开始时得到一个巨大的 fetch,要么得到一个单一的整体存储,否则您必须具有存储依赖项。以上是关于Flux 中的 AJAX:依赖状态更改时刷新存储的主要内容,如果未能解决你的问题,请参考以下文章