React Flux 实现调度链

Posted

技术标签:

【中文标题】React Flux 实现调度链【英文标题】:React Flux implement dispatch chain 【发布时间】:2015-06-29 04:38:50 【问题描述】:

我正在尝试将 React 与 Flux 架构一起使用,但偶然发现了一个我无法处理的限制。 问题如下:

    有一个监听事件的商店。事件具有对象 ID。如果需要,我们需要获取对象并将其选中。 如果商店没有具有此 ID 的对象 - 将对其进行查询。在回调中,我们调度另一个事件来存储,该事件负责选择。 如果商店有对象 - 我想分派选择事件,但我不能因为分派正在进行中。

到目前为止我想出的最佳解决方案是将内部调度包装在 setTimeout(f, 0) 中,但它看起来很吓人。

实际上这个问题很普遍——如果每个新的调度都是基于之前的调度处理结果,我应该如何组织调度链而不使用调度嵌套(不违反当前的 Flux 限制)。

有人有解决此类问题的好方法吗?

var selectItem(item) 
    AppDispatcher.dispatch(
        actionType: AppConstants.ITEM_SELECT,
        item: item
    );


// Item must be requested and selected.
// If it's in store - select it.
// Otherwise fetch and then select it.
SomeStore.dispatchToken = AppDispatcher.register((action) => 
    switch(action.actionType) 
        case AppConstants.ITEM_REQUESTED:
            var item = SomeStore.getItem(action.itemId);
            if (item) 
                // Won't work because can't dispatch in the middle of dispatch
                selectItem(item);
             else 
                // Will work
                $.getJSON(`some/$action.itemId`, (item) => selectItem(item));
            
    
;

【问题讨论】:

【参考方案1】:

如果 ITEM_SELECT 是另一个Store 将要处理的事件:

您正在寻找dispatcher.waitFor(array<string> ids): void,它允许您使用register() 返回的SomeStore.dispatchToken 来强制执行商店处理事件的顺序。

商店,假设我们称之为OtherStore,它将处理ITEM_SELECT 事件,应改为处理ITEM_REQUEST 事件,但首先调用dispatcher.waitFor( [ SomeStore.dispatchToken ] ),然后通过SomeStore 获取任何有趣的结果一个公共方法,比如SomeStore.getItem()

但是从您的示例来看,SomeStore 似乎对其内部状态没有做任何事情 ITEM_REQUEST,所以您只需将以下几行移动到 OtherStore 变化

// OtherStore.js
case AppConstants.ITEM_REQUESTED:
        dispatcher.waitFor( [ SomeStore.dispatchToken ] );// and don't even do this if SomeStore isn't doing anything with ITEM_REQUEST
        var item = SomeStore.getItem(action.itemId);
        if (item) 
            // Don't dispatch an event, let other stores handle this event, if necessary
            OtherStore.doSomethingWith(item);
         else 
            // Will work
            $.getJSON(`some/$action.itemId`, (item) => OtherStore.doSomethingWith(item));
        

同样,如果其他商店需要处理OtherStore.doSomethingWith(item) 的结果,他们也可以处理ITEM_REQUESTED,但在继续之前调用dispatcher.waitFor( [ OtherStore.dispatchToken ] )

【讨论】:

examples 没有显示它,但 SomeStore 实际上处理消息。你的方法很好,我想实际上解决了一个问题,但它一般不能解决问题。如果消息处理期间的 SomeStore 可以调度不同的操作类型,那么使用您的方法,您必须在整个端点存储中携带此逻辑。 我想说的是 SomeStore 不应该在消息处理期间调度不同的操作类型。 Flux 开发人员也这么认为,这就是为什么他们将waitFor 包括在他们的 Dispatcher 实现中可用的少数方法中。 waitFor 确实解决了一般问题。我认为您应该将事件链视为一种反模式,并重新考虑如何处理事件。也许包括您的具体示例的更多细节?【参考方案2】:

那么,在查看您的代码时,您是否在项目上设置了“已选择”属性,以便在您的 UI/组件中检查/选择它?如果是这样,只需将您已经在其中的那部分功能加入即可。

if(item) 
    item.selected = true;
    //we're done now, no need to create another Action at this point, 
    //we have changed the state of our data, now alert the components 
    //via emitChange()

    emitChange();

如果您想跟踪商店中当前选定的项目,只需将 ID 或对象作为私有 var,然后进行类似设置。

var Store = (function()

    var _currentItem = ;
    var _currentItemID = 1;

    function selectItem(item) 

          _currentItem = item;
          _currentItemID = item.id;
          emitChange();
    


    (function() 
         Dispatcher.register(function(action)
               case AppConstants.ITEM_REQUESTED:
                   var item = SomeStore.getItem(action.itemId);
                   if (item) 
                        selectItem(item);
                    else 
                   $.getJSON(`some/$action.itemId`, (item) => 
                        selectItem(item);   
                   
         );
    )();


    return 
         getCurrentlySelectedItem: function() 
              return _currentItem;
         ,
         getCurrentlySelectedItemID: function() 
              return _currentItemID;
         
    

)();

最终,您不必为所有内容创建操作。无论您正在操作的项目是什么,它都应该是某个域实体,并且管理该特定实体的状态是您的商店的工作。拥有其他内部函数通常是必需的,因此只需将 selectItem(item) 设为 Store 的内部函数,这样您就不必创建新的 Action 来访问或使用它。

现在,如果您有跨存储问题,并且另一个存储关心您初始存储中某些数据的某些特定更改,这就是 waitFor(ids) 函数的用武之地。它有效地阻止执行,直到第一个存储被更新,那么对方可以继续执行,保证对方Store的数据处于有效状态。

我希望这有意义并解决您的问题,如果没有,请告诉我,希望我能更好地归零。

【讨论】:

【参考方案3】:

您正在编写自己的调度程序吗? setTimeout(f, 0) 是个好技巧。我在最小通量here 中做同样的事情。那里没什么可怕的。 javascript 的concurrency model 非常简单。

更强大的通量调度程序实现应该为您处理。

【讨论】:

微通量实现万岁 :) 不是已经没有很多了,但这是我的 captray.github.io/vanilla-flux 关于这个问题,可以在商店内创建新的操作,但听起来您正在尝试在调度程序完成调度之前这样做。但是,在那里看到“选择/选择”这个词听起来像 UI 状态正在蔓延到您的应用程序状态。可能值得重新考虑或重新调整您的操作。很难说没有更多的上下文。 @z5h 不,我试图坚持使用fb implementation。试图在网上搜索解决方案,但发现只有人说调度链真的很糟糕。我不喜欢 setTimeout(f, 0) 的诡计,因为看起来我正在使用解决方法来做同样的事情(我实际上并不认为这很糟糕,但倾向于相信聪明人)。感谢 MDN 链接!看来我真的需要一些基础知识! @captray 您的实现与 fb 的实现非常相似(就功能而言)。关于问题:确切地说,我正在尝试从另一个分派中分派,因为只有第二个分派地点有数据要分派。我将选择存储在 App State 中,因为整个应用程序都有当前选择的概念。也许这真的不是什么好主意。 其实一模一样。我只是包含了一个自定义 JS 事件和一个 Reactor,而不是依赖于 Event Emitter 或其他事件解决方案。同样,如果没有看到代码,很难说。上面的第 1 点说您的商店正在收听一个事件?当 Store 向 Dispatcher 注册时,Store 应该只通过提供给 Dispatcher 的回调函数接收数据负载。您的 Store 是否在监听常规 Dispatcher -> Store -> 组件流之外的其他事件?

以上是关于React Flux 实现调度链的主要内容,如果未能解决你的问题,请参考以下文章

React 和 Flux:“在调度中间调度”以显示来自 API 调用的错误消息

React Native + Flux,无响应的调度程序

react/flux- 子组件用户事件 - 一切都应该通过调度程序路由吗

在 Flux/React 中调度级联/依赖异步请求

React、Flux、React-Router 调度错误 - 可能的批处理更新解决方案?

从 react-native-router-flux 事件调度操作到 redux