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:依赖状态更改时刷新存储的主要内容,如果未能解决你的问题,请参考以下文章

React Js - Flux 中的状态管理

React Flux:存储依赖项

Flux Utils 存储和异步定时器

Flux 和 React JS 中的依赖动作

在 Flux 中处理 ajax 请求的最佳方式?

区分 Flux 中的存储更改?