不同的子组件通过同时调用setState弄乱了父状态

时间:2019-01-06 17:31:22

标签: javascript reactjs setstate

我正在基于间隔创建游戏。父组件创建一个间隔,并让其子组件注册每个间隔触发的回调。

我的设置看起来像

<ParentComponent>
  <Child1 />
  <Child2 />
  ...
</ParentComponent>

父母使用React.cloneElement并为每个孩子提供一种方法

registerCallback( callback, id ) {
  const { callbacks } = this.state;
    this.setState({
      callbacks: callbacks.concat({
        callback,
        id
      })
    })
  }

这种方法效果很好。但是现在我有两个孩子从registerCallback()内部调用componentDidMount()方法。

现在只有<Child2 />将其回调存储在父状态中。

当我像这样延迟registerCallback()方法的调用

setTimeout(() => {
  registerCallback(callbackFunction, id)
}, 50 )

工作正常。但这感觉是一个非常错误的破解,尤其是因为我需要“猜测”状态需要更新多长时间。

所以我的结论是状态更新太慢了,而是在组件外部定义了一个数组,然后更新了它。奇怪的是,如果我对该数组进行了变异,它就可以正常工作。

所以如果我用 array.push({callback, id}) 我的数组看起来像[callback1, callback2]

但是由于我不想改变数组,所以我尝试了 array.concat({callback, id}) 这给我留下了[]

我的状态看起来像[array2]

我该如何改善在父级中的方法,使子级可以随时随地调用registerCallback()

1 个答案:

答案 0 :(得分:2)

您已经正确推断出问题是setState是异步的,因此到第二个子对象调用registerCallback时,第一个子对象的状态更改尚未传播到this.state。试试:

registerCallback( callback, id ) { this.setState(({ callbacks }) => ({ callbacks: callbacks.concat({ callback, id }) }) }

您将一个函数作为第一个参数传递给setState。此函数接收当前状态(以上为{ callbacks }的结构),并应返回新状态。该函数将立即或在先前的状态更改传播后被调用,因此可以确保其状态是最新的。因此,避免使用可能过时的this.state

这是一篇有关它的文章:

https://medium.com/@wereHamster/beware-react-setstate-is-asynchronous-ce87ef1a9cf3