`render` 和 `componentDidMount` 之间 Store 中的竞争条件
Posted
技术标签:
【中文标题】`render` 和 `componentDidMount` 之间 Store 中的竞争条件【英文标题】:Race Condition in Store between `render` and `componentDidMount` 【发布时间】:2015-12-04 20:53:38 【问题描述】:在 componentDidMount
中设置 Store 侦听器,同时不调用 setState
或 forceUpdate
(从而触发立即重新渲染)是否安全?
在 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 中的竞争条件的主要内容,如果未能解决你的问题,请参考以下文章
在哪里放置 @Scripts.Render 和 @Styles.Render
React.render和reactDom.render的区别
Django - render()、render_to_response() 和 direct_to_template() 有啥区别?