React组件的道具如何异步更新?

时间:2018-07-16 03:22:24

标签: javascript reactjs properties

深入研究后,我以为我理解了组件的props不能由组件本身更新,尽管可以由父组件更改。

但是我在react docs中找到了这一部分:

  

React可以将多个setState()调用批处理为一个更新,以提高性能。   由于this.props和this.state可以异步更新,因此您不应依赖于它们的值来计算下一个状态。

因此,我试图以我所理解的来解释此摘录所说的内容。如果道具已更新,则更新必须来自父组件。如果更新来自父组件,为什么它可能是异步的?

2 个答案:

答案 0 :(得分:0)

请检查zloctb here给出的答案

// 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
}));

答案 1 :(得分:0)

  

如果道具已更新,则更新必须来自父组件。

是的。

  

如果更新来自父组件,为什么它可能是异步的?

您可以将带有道具的父母的setState()传递给孩子,并在孩子的代码中的某个地方调用它。而且它仍然可能是异步的。我想出了以下示例:

class Container extends React.Component {
  state = { containerState: 0 };
  render() {
    return (
      <Component
        containerState={this.state.containerState}
        setContainerState={this.setState.bind(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 
}));

查看示例的完整代码:

<!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>

注意:并非所有setState()调用都是异步的。在我的示例中,如果您在setInterval(this.update, 2000)中取消对componentDidMount的注释,您将不会得到相同的行为。这样setState()调用是同步的。

请参见以下链接以说明发生这种情况的原因:When and why are setState() calls batched?

简而言之,这是因为

  

当前(React 16和更早版本),默认情况下仅批处理React事件处理程序中的更新。有一种不稳定的API可以在极少数情况下在需要时强制在事件处理程序之外进行批处理。