在React中将子状态传递给父状态,但父状态滞后于子状态

时间:2019-11-23 11:02:08

标签: reactjs input event-handling parent-child lag

在浏览了包括Stackoverflow在内的各种网站之后,我终于弄清楚了如何将状态从子级传递到父级。我必须做两次,因为我的结构是: <NavButtons>个孩子 <Nav>父级 <App>祖父母

这个想法是将点击事件转换为<NavButtons>中的字符串,将该字符串保存为状态,将状态传递给<Nav>,然后再次将其传递给<App>使用字符串更新视图。 问题在于,尽管子状态在单击时立即更新,但父状态仅在再次单击后更新,而祖父母将仅在再次单击后更新。这似乎是非常奇怪的行为,我似乎无法在任何地方找到解决方案。我觉得这可能是由于我对生命周期方法中事物发生顺序的理解不充分。

NavButtons (子级)

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

导航(父级)

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

应用(祖父母)

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

1 个答案:

答案 0 :(得分:3)

问题在于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个不同组件的状态下跟踪相同的值,这是不必要的,并且很难维护。

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

所以您将拥有:

  • 一个App组件,它跟踪active的值,并将其与函数Nav一起传递给setActive,该函数可以使用进行更改

  • 一个Nav组件,将这两个道具传递给其NavButton子代

  • NavButton子元素,这些子元素使用active道具来确定它们是否处于活动状态,并在每次单击时调用setActive函数。