简体   繁体   English

React组件的props如何异步更新?

[英]How is React component's props updated asynchronously?

After digging around I thought I understood that a component's props cannot be updated by the component itself, although can be changed by the parent component. 深入研究之后,我以为我理解了组件的props不能由组件本身更新,尽管可以由父组件更改。

However I found this one section in react docs: 但是我在react docs中找到了这一部分:

React may batch multiple setState() calls into a single update for performance. React可以将多个setState()调用批处理到单个更新中以提高性能。 Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state. 由于this.props和this.state可以异步更新,因此您不应依赖于它们的值来计算下一个状态。

So I am trying to interpret what this excerpt says with I have understood. 因此,我试图以我所理解的来解释该摘录所说的内容。 If props is updated, then the update must come from parent component. 如果道具已更新,则更新必须来自父组件。 If the update comes from a parent component, why is it possibly asynchronous? 如果更新来自父组件,为什么它可能是异步的?

Please check the answer given by zloctb here 请在此处检查zloctb给出的答案

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

If props is updated, then the update must come from parent component. 如果道具已更新,则更新必须来自父组件。

That is correct. 那是对的。

If the update comes from a parent component, why is it possibly asynchronous? 如果更新来自父组件,为什么它可能是异步的?

You can pass parent's setState() with props to a child and call it somewhere in child's code. 您可以将带有道具的父母的setState()传递给孩子,并在孩子的代码中的某个地方调用它。 And it still may be asynchronous. 而且它仍然可能是异步的。 I came up with the following example: 我想出了以下示例:

class Container extends React.Component {
  state = { containerState: 0 };
  render() {
    return (
      <Component
        containerState={this.state.containerState}
        setContainerState={this.setState.bind(this)}
      />
    );
  }
}

And then, in child Component , you have code like this: 然后,在子Component ,您具有如下代码:

this.props.setContainerState({
  containerState: this.props.containerState + 1
});

// You want to use updated this.props.containerState here, but you can't,
// because parent's setState() MAY BE deferred
this.props.setState({ componentStateUpdatedWithObject: this.props.containerState });

// Now if you use the function instead, you can expect to get updated props
// as a second argument
this.props.setState((state, props) => ({ 
  componentStateUpdatedWithFunction: props.containerState 
}));

See the full code for my example: 请参阅完整的代码作为我的示例:

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <div id="root"></div> </body> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin ></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin ></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script type="text/babel"> class Container extends React.Component { state = { containerState: 0 }; render() { return ( <Component containerState={this.state.containerState} setContainerState={this.setState.bind(this)} /> ); } } class Component extends React.Component { state = { componentStateUpdatedWithObject: [0, this.props.containerState], componentStateUpdatedWithFunction: [0, this.props.containerState] }; log = title => { console.log(title); console.log("- containerState", this.props.containerState); console.log( "- componentStateUpdatedWithObject", this.state.componentStateUpdatedWithObject ); console.log( "- componentStateUpdatedWithFunction", this.state.componentStateUpdatedWithFunction ); console.log("=========="); }; update = () => { this.log("before update"); this.props.setContainerState({ containerState: this.props.containerState + 1 }); this.log("after setContainerState"); this.setState({ componentStateUpdatedWithObject: [ this.state.componentStateUpdatedWithObject[0] + 1, this.props.containerState ] }); this.log("after setState with object"); this.setState((state, props) => ({ componentStateUpdatedWithFunction: [ state.componentStateUpdatedWithFunction[0] + 1, props.containerState ] })); this.log("after setState with function"); }; componentDidMount() { // setInterval(this.update, 2000); } render() { this.log("on render"); console.log("---------------------------------------------"); return ( <div> <div>containerState: {this.props.containerState}</div> <div> componentStateUpdatedWithObject:{" "} {this.state.componentStateUpdatedWithObject.join(", ")} </div> <div> componentStateUpdatedWithFunction:{" "} {this.state.componentStateUpdatedWithFunction.join(", ")} </div> <button onClick={this.update}>UPDATE</button> </div> ); } } ReactDOM.render(<Container />, document.getElementById("root")); </script> </html> 

NOTE: Not all setState() calls are asynchronous. 注意:并非所有setState()调用都是异步的。 In my example if you uncomment setInterval(this.update, 2000) in the componentDidMount , you won't get the same behavior. 在我的例子,如果你取消注释setInterval(this.update, 2000)componentDidMount ,你不会得到相同的行为。 This way setState() calls are synchronous. 这样setState()调用是同步的。

See this link for explanation why this happens: When and why are setState() calls batched? 请参阅此链接以了解发生这种情况的原因: setState()调用何时以及为什么批量处理?

In short, this is because 简而言之,这是因为

Currently (React 16 and earlier), only updates inside React event handlers are batched by default. 当前(React 16和更早版本),默认情况下仅批处理React事件处理程序中的更新。 There is an unstable API to force batching outside of event handlers for rare cases when you need it. 有一个不稳定的API可以在极少数情况下在需要时强制在事件处理程序之外进行批处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM