【问题标题】:How to avoid dispatching in the middle of a dispatch如何避免在调度过程中调度
【发布时间】:2015-08-02 03:22:00
【问题描述】:

在我的 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 || [];
},

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

谢谢!

【问题讨论】:

  • 这是我从 Flux 切换到 Redux 的原因之一。
  • @ian-walker-sperber 这个问题已经被浏览了很多次。你有没有机会选择正确的答案?
  • @RyanRho 你如何处理乱码?

标签: javascript reactjs reactjs-flux


【解决方案1】:

您可以使用 Flux waitFor() 函数代替 setTimeout

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

【讨论】:

    【解决方案2】:

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

    所以这个:

    this.emit(CHANGE_EVENT);
    

    成为

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

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

    【讨论】:

    • 我遇到了同样的问题,也不得不这样做。还有其他方法可以解决这个问题吗?确实有点hacky。你最后是怎么解决的?
    • @JurgenVandw 在这个项目上,我坚持使用我的setTimeout 解决方案,因为它已接近完成,但我建议按照@andykenward 的建议尝试waitFor()。在随后的项目中,我一直在使用带有 thunk 的 redux,并且在处理这种情况和类似情况时更容易:)
    • setTimeout 是一个讨厌的把戏。今天,我在实施新商店后遇到了同样的问题:所有以前的商店都使用您的“有问题”的实施并且从未遇到过问题。可能是某个地方的错误实现
    【解决方案3】:

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

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

    正如 Amida 和该答案的 cmets 所指出的那样,是否从商店发出 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 调用放入您的操作创建器中,然后从那里分派“成功/失败”操作。

    【讨论】:

      【解决方案4】:

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

      RatingActions.fetchAll.defer(options);
      

      【讨论】:

        【解决方案5】:

        就我而言,我通过操作/操作创建者获取数据。 store 只是一个接收动作负载的转储位置。

        这意味着我将在一个操作中“获取所有”,然后将结果传递给商店,商店将对其执行任何操作,然后发出一个更改事件。

        有些人像我一样考虑使用商店,有些人则像你一样思考。 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 工程师)在此解释她对如何使用商店的看法。

        【讨论】:

        • 数据获取仍然发生在一个动作中,如果数据还不存在,我们只是允许存储启动一个动作。在组件中,我们不知道我们在本地是否有正确的数据。即使该操作被放置在组件中,它也必须使用相同的方法来响应商店的更改(因为日期已更改),因此仍处于调度的中间。
        • 您的商店不应触发操作。看看这些指南。 pbs.twimg.com/media/CDyAQ-BW0AESYP1.png:large没找到作者。
        • 根据 Facebook 的 Flux 存储库的维护者 Bil​​l Fisher 的说法,这是一种误解。请参阅他在 stackoverflow.com/questions/25860642/… 的评论,其中他明确指出它 好的(我认为它也不是很长时间)。虽然仍然只是一种架构选择
        • 也许,在我所链接的对话中,景琛也是这么说的,但我认为如果你不这样做,你就不会有问题导致你来到这里。我从来没有面对过。至少我认为你应该尝试看看这是否能解决你的问题。
        • 这里有些微妙之处。 Bill Fisher 的意思是,商店可以进行异步工作以响应操作(很可能是 ajax 请求),然后在 异步回调 中触发操作。这不会在分派中间分派,因为在发出 ajax 请求后,执行将继续同步,直到分派完成。所以商店不能在对动作的同步响应中分派新事件,但它可以异步执行。如果你选择。我更喜欢在动作创建者中做异步工作。
        猜你喜欢
        • 2016-04-22
        • 1970-01-01
        • 2018-02-06
        • 1970-01-01
        • 2017-04-12
        • 2017-03-13
        • 2019-12-15
        • 2013-03-24
        • 1970-01-01
        相关资源
        最近更新 更多