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

Posted

技术标签:

【中文标题】React、Flux、React-Router 调度错误 - 可能的批处理更新解决方案?【英文标题】:React, Flux, React-Router Dispatch Error - Possible batchUpdates solution? 【发布时间】:2015-04-29 20:23:41 【问题描述】:

好的,所以我遇到了 reactjs、flux architecture 和 react-router 的问题。我有以下路线(只是部分路线):

<Route name="prepare-seniors">
  <Route name="senior" path=":candidateId" handler=SeniorCandidate>
    <DefaultRoute handler=SeniorProfile/>
    <Route name="senior-recommendations" path="recommends">
      <DefaultRoute handler=SeniorRecommends/>
      <Route name="senior-rec-new" path="new"/>
    </Route>
  </Route>
</Route>

Senior Profile 视图调用 API 来加载个人数据。当您导航到 Recommends 视图时,个人的 ID 用于进行另一个调用以加载推荐。如果我实际上首先查看个人资料页面并导航到推荐页面,它会非常有用。但是,如果我进行硬重载,我会得到:

Uncaught Error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.

我意识到这是因为在第一个 API 返回后调用了调度,该 API 退出并开始更新组件。在它完成之前,推荐页面调用它的 API 并尝试分发它的结果。我在一个论坛上读到 React.addons.batchUpdates 是解决这个问题的一种方法,但我不知道如何让它工作。 GitHub Batch Updates Issue 和此处的另一个链接讨论类似Trying to use waitFor 的内容。第一个建议通过添加以下内容来调整调度程序:

// assuming a var `flux` containing your Flux instance...
var oldDispatch = flux.dispatcher.dispatch.bind(flux.dispatcher);
flux.dispatcher.dispatch = function(action) 
  React.addons.batchedUpdates(function() 
  oldDispatch(action);
  );
;

但我无法完成这项工作。也许我只是不正确地实施它。

我已阅读 Chat 和 TodoMVC 示例。我了解在聊天示例中如何使用 waitFor ......但是它们都使用相同的 API,因此很明显一个会等待另一个。我的问题涉及 API 和调度之间的竞争条件......我不认为 setTimeout 是一个好的解决方案。

我需要了解如何设置调度程序,以便它将调度或 API 调用排队。或者告诉每个组件对其数据进行 API 调用的更好方法,所以我什至没有调度问题。

哦,这是我的 Dispatcher.js 文件,你可以看看它是如何设置的:

'use strict';

var flux = require('flux'),
 Dispatcher = require('flux').Dispatcher,
 React = require('react'),
 PayloadSources = require('../constants/PayloadSources'),
 assign = require('object-assign');

//var oldDispatcher = flux.Dispatcher.dispatch.bind(AppDispatcher);
//
//flux.dispatcher.dispatch = function(action) 
//  React.addons.batchedUpdates(function() 
//    oldDispatcher(action);
//  );
//;


var AppDispatcher = assign(new Dispatcher(), 
   handleServerAction: function(action) 
     var payload = 
       source: PayloadSources.SERVER_ACTION,
       action: action
     ;
     this.dispatch(payload);
   ,

   handleViewAction: function(action) 
     if (!action.type) 
       throw new Error('Empty action.type: you likely mistyped the action.');
     

     var payload = 
       source: PayloadSources.VIEW_ACTION,
       action: action
     ;
     this.dispatch(payload);
    
);
module.exports = AppDispatcher;

【问题讨论】:

【参考方案1】:

好的,首先我只想说我目前正在学习 React 和 Flux,但绝不是专家。不过我还是要试一试:

根据您所说,听起来您有 2 个异步操作触发关闭,然后当它们返回时尝试发送调度消息 调度调用由 2 个独立的 aync Web 服务调用触发两次而引起的问题。

我认为在这种情况下批量更新不会有帮助,因为它在很大程度上取决于时间(即,如果它已将重新渲染排队并等待第二次调用时执行的机会,它可以批处理成功,但是如果它在更新中期出现,您将处于与现在完全相同的位置)。

我认为这里的实际问题来自这些操作的触发方式/时间(您在帖子末尾简要提到了这一点)。听起来您正在从 React 组件 Render 调用中触发 Actions(有点像延迟加载)。这几乎正​​是 Flux 试图避免的事情,因为这种模式很容易导致应用程序流程中的无限循环或奇怪的状态/模式。

正如您在描述中提到的,您可能需要预先触发此加载,而不是从单个组件中触发,据我所知,我建议在您的核心组件上调用 React.render 之前需要完成此操作。如果您查看示例聊天应用程序,他们会在 app.js 中执行类似的操作:

ChatExampleData.init(); // load example data into localstorage

ChatWebAPIUtils.getAllMessages();

React.render(
    <ChatApp />,
    document.getElementById('react')
);

所以我建议遵循上述模式,预先加载任何初始数据,同时向客户端显示加载微调器。

或者,根据您的技术堆栈,您可以最初在服务器上呈现(节点示例 here ReactJS.Net 的 .Net 版本),因此您在客户端启动时没有延迟(您也可以避免所有如果这是一个问题,那么搜索引擎索引的有趣业务(顺便说一句,我没有尝试过这些,只是阅读它们)。

【讨论】:

是的,我注意到他们在聊天中以这种方式处理它,但我不确定当组件根据其父组件状态/信息发生更改时它会如何工作。我的意思是,如果我每次都调用完全相同的 API,这会起作用,但是当我调用的 API 会根据给定的道具而变化时呢……即我使用特定数据作为搜索参数?遗憾的是,我不能为这个项目使用服务器端渲染,因为我工作的公司不想在服务器上使用节点,而只是想使用 grunt 构建静态文件,然后可以提供给服务器 哦,我要提到的是,在调用 API 之前,我删除了在我的操作创建器中调用的子操作,它开始工作。主要是因为一个 API 很慢而且我不再有冲突。基本上有一个动作会说从服务器请求数据并设置一个微调器,并且由于大约在我收到调度错误的同时调用 API。但这仍然没有回答我的问题,即当我有两个大约同时返回的 API 时该怎么办,我仍然会收到调度错误。 抱歉最后的评论。我不是在 render 方法中尝试这样做,而是在 componentWillRecieveProps 方法中,所以我不确定这是否仍然是一种反模式......看看他们的图表唯一应该击中从视图中的动作创建者是“用户交互”,所以这仍然可能是错误的。 是的,我同意可能从 componentWillReceiveProps 中删除该操作。 我刚刚找到了一些关于async loading data into a component 的文档,这就是你想要做的吗?如果此数据需要存储在商店中,我认为您可能需要将此状态向上推到父视图控制器组件中,如 here 所述,以便生成一个操作来加载所有相关数据。您实际上可能需要使这两个调用相互依赖(因为听起来它们都有助于组件的新状态)。

以上是关于React、Flux、React-Router 调度错误 - 可能的批处理更新解决方案?的主要内容,如果未能解决你的问题,请参考以下文章

React-router (0.13) + Flux - 如何将通量类实例放入 willTransitionTo 挂钩?

将 this.state 传递给 react-router 1.x / React, Flux 中的子级

react-router:从组件或存储更改路由

React + Flux:动作可以返回值吗? (例如最后创建的 id)

react学习

React JS 同构渲染