在 React 中将状态从 child 传递到 parent,但 parent 的状态落后于 child 的状态

Passing state from child to parent in React, but state of parent lags behind state of child

在浏览了包括 Whosebug 在内的各种网站之后,我终于弄清楚了如何将状态从 child 传递到 parent。我必须做两次,因为我的结构是: <NavButtons> child <Nav> parent <App>盛大parent

想法是将点击事件转换为 <NavButtons> 中的字符串,将该字符串保存到状态,将状态传递给 <Nav>,然后再次传递给 <App>它将使用字符串来更新视图。 问题在于,虽然 child 状态在点击时立即更新,但 parent 状态只会在再次点击后更新,然后 grandparent 只会在再次点击后更新。这似乎是非常奇怪的行为,我似乎无法在任何地方找到解决方案。我觉得这可能是因为我对生命周期方法中事情发生的顺序理解不足。

NavButtons (child)

class NavButtons extends React.Component {
  constructor(props) {
    super(props);
    this.state = { active: "" };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(num) {
    const btnArr = ["overview", "movement", "distribution"];
    this.setState({ active: btnArr[num] });
    this.props.getState(this.state.active);
  }

  render() {
    console.log("child says " + this.state.active);
    return (
      <div className="button-container">
        <ul className="nav-buttons">
          <li className="overview" onClick={() => this.handleClick(0)}>
            Overview
          </li>
          <li className="movement" onClick={() => this.handleClick(1)}>
            Movement
          </li>
          <li className="distribution" onClick={() => this.handleClick(2)}>
            Distribution
          </li>
        </ul>
      </div>
    );
  }
}

导航 (parent)

class Nav extends React.Component {
  constructor(props) {
    super(props);
    this.state = { navState: "" };
    this.passNavState = this.passNavState.bind(this);
  }

  passNavState(nav) {
    this.setState({ navState: nav });
    this.props.NavStateUpdate(this.state.navState);
  }


  render() {
    console.log("parent says "+this.state.navState)
    return (
      <div className="nav-container">
        <img src={Logo} alt="Tharsus logo" />
        <NavButtons getState={this.passNavState} />
        <DatePicker />
      </div>
    );
  }
}

App (grandparent)

class App extends React.Component {
  constructor() {
    super();

    this.state = {
      appDate: moment().format("1995-12-25", "YYYY-MM-DD"),
      apiData: "",
      navState: ""
    };
    this.getNavState = this.getNavState.bind(this);
  }

  getNavState(nav) {
    this.setState({ navState: nav });
  }

  componentDidMount() {
    const requestData = axios({
      url: "https://tharsus-interview-api-v1.azurewebsites.net/data/2019-10-19",
      method: "get",
      headers: {
        "content-type": "application/json"
      }
    })
      .then(response => {
        this.setState({ apiData: response });
      })
      .catch(err => {
        console.log(err);
      });
  }

  render() {
    console.log("grandparent says " + this.state.navState);
    return (
      <div className="App">
        <Nav NavStateUpdate={this.getNavState} />
        {this.state.navState === "movement" ? (
          <div className="chart-container">
            <MovementDataDisplay
              Data={[3.3176110809420893, 4.18238891905791]}
              Title="Site Hours"
            />
            <MovementDataDisplay
              Data={[3.9176110809420893, 4.18238891905791]}
              Title="Moving Hours"
            />
          </div>
        ) : (
          " "
        )}
      </div>
    );
  }
}

问题在于 setState 不会立即生效(参见 docs)。考虑一下您的这段代码片段:

    this.setState({ active: btnArr[num] });
    this.props.getState(this.state.active);

调用 setState 后,状态可能(并且很可能会)仍然具有旧状态的值。它只会在以后更新。因此,当您调用 this.props.getState(this.state.active) 时,您传递的是状态的旧值。

您可以快速破解,将正确的值传递给两种方法,如下所示:

    const value = btnArr[num]
    this.setState({ active: value });
    this.props.getState(value);

...这会起作用,但这不是 React 中做事的优雅方式。您在 3 个不同组件的状态下跟踪相同的值,这是没有必要的,而且很难维护。

如果您需要 child 使用与 parent 相同的值,我建议您稍微重构一下代码。让 App 跟踪 active 导航元素,然后将该值作为 prop 传递给 Nav 元素。从 Nav 元素,您还可以将 prop 的值传递给 child 元素。然后,保证所有三个组件始终具有相同的值,并且您的代码会更漂亮,因为您只将该值保持在一种状态。

所以你会:

  • 一个跟踪 active 值并将其传递给 NavApp 组件,以及一个函数 setActive,可以用来改变它

  • 一个 Nav 组件将这两个属性传递给它的 NavButton children

  • NavButton child 元素使用 active 属性判断是否激活,点击时调用 setActive 函数.