Flux中的AJAX:当依赖状态发生变化时刷新存储

时间:2015-01-31 23:29:02

标签: ajax reactjs-flux

我正在使用MartyJS构建一个Flux应用程序(它非常接近“vanilla”Flux并使用相同的底层调度程序)。它包含具有固有依赖关系的商店。例如,UserStore跟踪当前用户,InstanceStore跟踪当前用户拥有的数据实例。实例数据是异步从API获取的。

问题是当用户更改时,InstanceStore的状态如何处理。

我开始相信(例如,阅读@fisherwebdev在SO上的答案),最适合在动作创建器功能中发出AJAX请求,并在动作中产生AJAX“成功”结果,从而导致商店改变。

因此,为了获取用户(即登录),我正在动作创建器函数中进行AJAX调用,当它结算时,我正在向用户发送RECEIVE_USER动作作为有效负载。 UserStore会听取此信息并相应地更新其状态。

但是,如果用户被更改,我还需要重新获取InstanceStore中的所有数据。

选项1 :我可以在RECEIVE_USER中收听InstanceStore,如果是新用户,则会触发AJAX请求,从而创建另一个操作,这反过来导致InstanceStore更新。这个问题就是它感觉像是级联动作,虽然技术上它是异步的,所以调度员可能会允许它。

选项2 :另一种方法是让InstanceStore听取UserStore发出的更改事件然后执行请求动作,但这也是错误的。

选项3 :第三种方式是动作创建者编排两个AJAX调用并分别调度这两个动作。但是,现在动作创建者必须非常了解商店之间的相互关系。

Where should ajax request be made in Flux app?中的一个答案让我觉得选项1是正确的,但Flux文档也暗示商店触发行动并不好。

3 个答案:

答案 0 :(得分:1)

像选项3这样的东西对我来说似乎是最干净的解决方案,其次是选项1.我的推理:

选项2偏离了处理商店之间依赖关系的预期方式(waitfor),并且您必须在每个更改事件之后检查以确定哪些是相关的以及哪些可以被忽略,或者开始使用多个事件类型;它会变得非常混乱。

我认为备选方案1是可行的;如果您链接的帖子中为Bill Fisher remarked,则可以从商店内进行API调用,只要通过调用新操作处理结果数据即可。但是,确定并不一定意味着理想,如果您可以在一个地方(即ActionCreators)收集所有API调用和操作启动,那么您可能会更好地分离关注点并减少级联。这与选项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;

    ...
  }
});

答案 1 :(得分:0)

选项1似乎是概念上最好的选择。有2个单独的API调用,因此您有2组事件。

在少量代码中发生了很多事件,但Flux始终使用简单的标准Action-> Store-> View方法。一旦你做了一些聪明的事情(比如选项2),你就改变了。如果其他开发人员再也无法安全地认为任何Action流程与其他每个开发人员的工作流程完全相同,那么您已经失去了Flux的巨大优势。

虽然它不是代码中最短的方法。 MartyJS看起来好像比Facebook自己的Flux库更整洁了!

另一种选择;如果登录必须始终刷新InstanceStore,为什么登录API调用也不包括所有InstanceStore数据?

(并且更进一步;为什么有两个独立的商店?它们看起来非常强烈地耦合,并且没有理由你仍然无法调用InstanceStore API而无需重新调用登录)

答案 2 :(得分:0)

我通常使用承诺来解决这种情况。 例如:

// 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 ) { ... });
    }
});