`render` 和 `componentDidMount` 之间 Store 中的竞争条件

Posted

技术标签:

【中文标题】`render` 和 `componentDidMount` 之间 Store 中的竞争条件【英文标题】:Race Condition in Store between `render` and `componentDidMount` 【发布时间】:2015-12-04 20:53:38 【问题描述】:

componentDidMount 中设置 Store 侦听器,同时不调用 setStateforceUpdate(从而触发立即重新渲染)是否安全?

在 React 中常见的例子似乎是在监听之前同步 getInitialState 中的 Store 状态或拉取存储数据在 render 中,然后在 componentDidMount 中开始监听。在componentDidMount 中渲染和设置侦听器之间的时间间隔内,是什么防止了 Store 更改丢失?

如果子组件在其componentDidMount 中采取同步操作来更改存储,父组件会不会错过此更改?

如果子组件在componentDidMount 中发出异步操作,是否有可能以父组件忽略的方式更改 Store?

React 组件生命周期中是否存在异步事件可以进入的空白?

来自抢占式异步编程背景,javascript 中缺乏显式同步保护,而在协作异步编程中通常是不必要的,这真的让我感到不安。

【问题讨论】:

【参考方案1】:

经过一些测试和挖掘源代码后,我得出的结论是,在初始组件生命周期的任何地方都无法触发异步事件(getInitialState -> componentWillMount -> render -> Children -> componentDidMount)。

从可以更改 Store 的子组件调用的同步事件肯定会在父组件调用 componentDidMount 之前解决,并导致视图和数据不同步。

我得出的结论是,不在componentWillMount 中注册 Store 侦听器的唯一原因是它们不应该在同构应用程序的服务器上运行。在我看来,这不是充分的理由,因为它注册了不按文档顺序的侦听器(这可能会影响性能),并且有可能在初始渲染期间丢失对 Store 的同步更改。从概念上讲,这也是错误的地方。

我打算在 componentWillMount 中注册我的听众,并附上浏览器测试:

componentWillMount: function () 
  if (typeof window !== "undefined") 
    // register listener in browser only
  

【讨论】:

或在每个组件上设置一个静态方法,该方法从您的 url 获取 store 和 params,然后收集 stores 方法(这将返回 Promise)。 @limelights 我不完全理解您的评论,但我认为您可能在谈论 AJAX 商店,而我在谈论 Flux 商店。 通量存储的常见做法是在从您的侦听器调用的函数中使用 'getStateFromStores()` 或类似函数 - 从存储中获取 state。这样,如果您错过了商店中的 x 个更新周期,您的获取将从商店获取最新状态,这意味着所有更改都已处理,然后重新呈现最新状态。 @wintvelt 这就是疯狂所在。您假设更新会在您错过的更新之后及时更新。 问题的根本原因可能在其他地方:让子组件在子组件的初始渲染周期中某处触发存储更改。恕我直言,这可以而且应该避免。让组件 + 所有子组件的渲染周期完成,然后让任何组件执行某些操作来触发存储更新。可以让有状态的孩子从商店阅读。有状态的孩子不能改变商店。在组件树中向上移动这些调用并将结果作为道具传递给子级。

以上是关于`render` 和 `componentDidMount` 之间 Store 中的竞争条件的主要内容,如果未能解决你的问题,请参考以下文章

谷歌正在显示空白页 - Reactjs

在哪里放置 @Scripts.Render 和 @Styles.Render

React.render和reactDom.render的区别

Django - render()、render_to_response() 和 direct_to_template() 有啥区别?

Vue 基础 - 自定义指令和 render 函数

Vue render函数认识和使用