简体   繁体   English

在 React 中将状态从孩子传递给父母,但父母的状态落后于孩子的状态

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

After trawling various websites including Stackoverflow, I finally figured out how to pass state from child to parent.在浏览了包括 Stackoverflow 在内的各种网站后,我终于想出了如何将状态从子级传递到父级。 I have to do it twice since my structure is: <NavButtons> child <Nav> parent <App> grandparent我必须做两次,因为我的结构是: <NavButtons> child <Nav> parent <App> grandparent

The idea is to turn a click event into a string in <NavButtons> , save that string to state, pass the state to <Nav> and then pass it along again to <App> which will use the string to update the view.这个想法是将点击事件转换为<NavButtons>中的字符串,将该字符串保存到状态,将状态传递给<Nav> ,然后再次将其传递给<App> ,后者将使用该字符串更新视图。 The problem is that although the child state updates immediately on click, the parent state only updates after another click, then the grandparent will only update after yet another click.问题是虽然子状态在单击时立即更新,但父状态只会在再次单击后更新,然后祖父状态只会在再次单击后更新。 This seems like very strange behaviour and I can't seem to find a solution anywhere.这似乎是非常奇怪的行为,我似乎无法在任何地方找到解决方案。 I feel it is probably due to my poor understanding of the order in which things are happening in the lifecycle methods.我觉得这可能是由于我对生命周期方法中事情发生的顺序的理解很差。

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

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

The problem is that setState does not immediately take effect (see docs ).问题是 setState 不会立即生效(请参阅docs )。 Consider this code fragment of yours:考虑一下你的这个代码片段:

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

After setState is called, the state may (and likely will) still have the value of the old state.在调用 setState 之后,该状态可能(并且可能会)仍然具有旧状态的值。 It will only be updated later.它只会在以后更新。 Therefore, when you call this.props.getState(this.state.active) , you are passing the old value of the state.因此,当您调用this.props.getState(this.state.active) ,您传递的是状态的旧值。

You could do a quick hack that passes the correct value to both methods, like this:您可以进行一个快速的 hack,将正确的值传递给这两种方法,如下所示:

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

...and that would work, but it's not an elegant way of doing things in React. ...这会起作用,但这不是在 React 中做事的优雅方式。 You are keeping track of the same value in the state of 3 different components, which is not necessary and can get hard to maintain.您在 3 个不同组件的状态中跟踪相同的值,这不是必需的,并且很难维护。

If you need the child to use the same value as a parent, I suggest you refactor your code a little bit.如果您需要孩子使用与父母相同的值,我建议您稍微重构一下代码。 Let App keep track of the active navigation element, then pass that value as a prop to the Nav element.App跟踪active导航元素,然后将该值作为道具传递给Nav元素。 From the Nav element, you can also pass the value of the prop to the child elements.Nav元素,您还可以将 prop 的值传递给子元素。 Then, all three components are guaranteed to have the same value at all times, and your code will be prettier since you only keep that value in one state.然后,保证所有三个组件始终具有相同的值,并且您的代码会更漂亮,因为您只将该值保留在一种状态。

So you would have:所以你会有:

  • An App component that keeps track of the active value, and passes it to Nav , along with a function, setActive , that can be used to change it跟踪active值并将其传递给NavApp组件以及可用于更改它的函数setActive

  • A Nav component that passes on both of these props to its NavButton children将这两个 props 传递给其NavButton子项的Nav组件

  • NavButton child elements that use the active prop to determine if they are active, and call the setActive function whenever they are clicked. NavButton子元素使用active属性来确定它们是否处于活动状态,并在单击它们时调用setActive函数。

Don't set state in child and parent for navstate, directly pass as arguments in callback till grandparent. 不要在navstate的子级和父级中设置状态,直接将其作为参数传递给回调,直到祖父母。 Set state only in grandparent and try. 仅在祖父母中设置状态,然后尝试。

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

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