如何避免在调度中间调度

时间:2015-05-20 18:00:18

标签: javascript reactjs reactjs-flux

在我的Flux架构的React应用程序中,我正在从商店检索数据,并且想要创建一个动作来请求该信息(如果它不存在)。但是我遇到了调度员已经调度的错误。

我想要的代码如下:

getAll: function(options) {
  options = options || {};
  var key = JSON.stringify(options);
  var ratings = _data.ratings[key];

  if (!ratings) {
    RatingActions.fetchAll(options);
  }

  return ratings || [];
}

但是,当调度程序已经调度某个操作时,间歇性地失败,并显示消息Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.。我经常提出请求以响应应用程序状态的变化(例如日期范围)。我发出请求的组件,以响应AppStore中的更改事件,具有以下内容:

getStateFromStores: function() {
  var dateOptions = {
    startDate: AppStore.getStartISOString(),
    endDate: AppStore.getEndISOString()
  };

  return {
    ratings: RatingStore.getAll(dateOptions),
  };
},

我知道事件链是一个Flux反模式,但我不确定哪种架构在尚不存在时检索数据更好。目前我正在使用这个可怕的黑客:

getAll: function(options) {
  options = options || {};
  var key = JSON.stringify(options);
  var ratings = _data.ratings[key];

  if (!ratings) {
    setTimeout(function() {
      if (!RatingActions.dispatcher.isDispatching()) {
        RatingActions.fetchAll(options);
      }
    }, 0);
  }

  return ratings || [];
},

什么是更好的架构,避免事件链接或调度程序错误?这真的是事件链吗?我只想根据应用程序设置的参数更改数据。

谢谢!

5 个答案:

答案 0 :(得分:13)

您可以使用Flux waitFor()函数而不是setTimeout

例如,您有2个商店注册到同一个调度程序并且有一个商店等待另一个商店首先处理该操作然后等待的一个商店可以更新并发送更改事件。请参阅Flux docs example

答案 1 :(得分:6)

我发生了特定错误,因为我的商店在操作调度期间发出了更改事件,而它仍然在侦听器中循环。这意味着由于商店中的数据更改而触发操作的任何侦听器(即组件)将中断调度。我通过在调度完成后发出更改事件来修复它。

所以这个:

this.emit(CHANGE_EVENT);

成了

var self = this;

setTimeout(function() { // Run after dispatcher has finished
  self.emit(CHANGE_EVENT);
}, 0);

还有点hacky(可能会重写所以不需要setTimeout)。打开解决架构问题的解决方案,而不是这个实现细节。

答案 2 :(得分:5)

您在上一次调度过程中收到调度的原因是,您的商店会在处理程序中为其他操作调度一个操作(调用操作创建者)同步。调度程序在技术上调度,直到所有已注册的回调都已执行。因此,如果您从任何已注册的回调中发送新操作,则会收到该错误。

但是,如果你做了一些异步工作,例如发出一个ajax请求,你仍然可以在ajax回调中调度一个动作,或者一般是异步回调。这是有效的,因为只要调用了异步函数,它就会立即继续执行该函数并将回调放在事件队列中。

正如Amida所指出的那样,在答案的评论中,是否要从商店发出ajax请求以响应某个动作,或者是否在商店中这样做,这是一个选择问题。关键是商店应该只改变其状态以响应操作,而不是在ajax / async回调中。

在您的特定情况下,如果您希望从商店进行ajax调用,可以通过此类商品的注册回调来举例说明:

onGetAll: function(options) {
    // ...do some work

    request(ajaxOptions) // example for some promise-based ajax lib
        .then(function(data) {
             getAllSuccessAction(data); // run after dispatch
        })
        .error(function(data) {
             getAllFailedAction(data); // run after dispatch
        });

     // this will be immediately run during getAllAction dispatch
     return this.state[options];
},
onGetAllSuccess: function(data) {
    // update state or something and then trigger change event, or whatever
},    
onGetAllFailed: function(data) {
    // handle failure somehow
}

或者您可以将ajax调用放入您的操作创建者并发送"成功/失败"那里的行动。

答案 3 :(得分:1)

您可以在调度程序中使用“延迟”选项。 在你的情况下,它会像:

RatingActions.fetchAll.defer(options);

答案 4 :(得分:0)

就我而言,我通过动作/动作创建者获取数据。商店只是一个接收行动有效载荷的转储场所。

这意味着我会在一个动作中“取消”,然后将结果传递给商店,商店会对它做任何事情,然后发出一个改变事件。

有些人考虑使用像我这样的商店,其他人则像你一样。 Facebook上的一些人使用“我的”方法:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51

我认为这可能会避免像这样处理你的商店的调度问题,但我可能错了。

这是一个有趣的讨论:https://groups.google.com/forum/#!topic/reactjs/jBPHH4Q-8Sc

Jing Chen(Facebook工程师)解释了她对如何使用商店的看法。