【问题标题】:AJAX in Flux: Refreshing stores when dependent state changesFlux 中的 AJAX:依赖状态更改时刷新存储
【发布时间】: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 文档也暗示存储触发操作不好。

【问题讨论】:

    标签: ajax reactjs-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,要么得到一个单一的整体存储,否则您必须具有存储依赖项。
        猜你喜欢
        • 2014-11-17
        • 2016-05-24
        • 2015-01-29
        • 1970-01-01
        • 2014-09-03
        • 1970-01-01
        • 1970-01-01
        • 2020-06-10
        • 2015-07-05
        相关资源
        最近更新 更多