【问题标题】:Passing state from child to parent in React, but state of parent lags behind state of child在 React 中将状态从孩子传递给父母,但父母的状态落后于孩子的状态
【发布时间】:2019-11-23 11:02:08
【问题描述】:

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

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

导航按钮(子)

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

【问题讨论】:

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


    【解决方案1】:

    问题在于 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 导航元素,然后将该值作为道具传递给Nav 元素。从 Nav 元素中,您还可以将 prop 的值传递给子元素。然后,保证所有三个组件始终具有相同的值,并且您的代码会更漂亮,因为您只将该值保持在一种状态。

    所以你会:

    • 一个 App 组件,它跟踪 active 值,并将其传递给 Nav,以及一个可用于更改它的函数 setActive

    • 一个 Nav 组件将这两个 props 传递给它的 NavButton 子组件

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

    【讨论】:

    • 除了上面推荐的操作,我完全同意,我想推荐快速浏览一下 React Hooks,你会看到一个很好的关注点分离方法,它可以帮助你更多地在组件之间共享状态。 reactjs.org/docs/hooks-intro.html
    • 感谢您全面周到的回复!
    • 但是App 怎么能跟踪状态呢?状态存储已单击的按钮,但单击事件必须发生在子项中...我看不到将信息向上传递的方法...我想我可以摆脱 NavButtons 而是将其嵌套在Nav 中,但这仍然意味着将状态从Nav 传递到App
    • 等等,也许我明白了!我可以在不使用状态的情况下传递一个值吗?事实上,我每次都将值存储在状态中,这会导致问题!我希望这是对的,我觉得我只是有一个脑电波哈哈
    • @erasebegin 正确!您不需要将其存储在所有 3 个组件的状态中。您可以在 App 中创建一个更改应用程序状态的函数,然后您可以将该函数向下传递给子组件。当点击 NavButton 时,它可以调用该函数,这将改变状态。
    猜你喜欢
    • 2019-05-07
    • 2018-06-29
    • 2020-04-06
    • 1970-01-01
    • 1970-01-01
    • 2017-01-17
    • 2018-08-04
    • 2019-10-20
    • 1970-01-01
    相关资源
    最近更新 更多