【问题标题】:React Parent component checkbox state updates with one step delay反应父组件复选框状态更新一步延迟
【发布时间】:2018-10-23 18:37:31
【问题描述】:

我有一个父组件:

import React, { Component } from "react";
import { Button } from "./Button";

export class Dashboard extends Component {
  constructor(props) {
    super(props);
    this.state = {
      numbers: [],
      disabled: false
    };

    this.setNum = this.setNum.bind(this);
  }

  setNum(num) {
    if (!this.state.numbers.includes(num)) {
      this.setState(prevState => ({
        numbers: [...prevState.numbers, num]
      }));
    } else if (this.state.numbers.includes(num)) {
      let nums = [...this.state.numbers];
      let index = nums.indexOf(num);
      nums.splice(index, 1);
      this.setState({ numbers: nums });
      console.log(this.state.numbers);
    }
    if (this.state.numbers.length >= 4) {
      this.setState({ disabled: true });
    } else if (this.state.numbers.length < 4) {
      this.setState({ disabled: false });
    }
  }

  render() {
    return (
      <div className="board-container">
        <div className="board">
          <div className="row">
            <Button
              id="1"
              numbers={this.state.numbers}
              onChange={this.setNum}
              disabled={this.state.disabled}
            />
            <Button
              id="2"
              numbers={this.state.numbers}
              onChange={this.setNum}
              disabled={this.state.disabled}
            />
            <Button
              id="3"
              numbers={this.state.numbers}
              onChange={this.setNum}
              disabled={this.state.disabled}
            />
            <Button
              id="4"
              numbers={this.state.numbers}
              onChange={this.setNum}
              disabled={this.state.disabled}
            />
          </div>
        </div>
      </div>
    );
  }
}

...和一个子组件:

import React, { Component } from "react";

export class Button extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isChecked: false
    };

    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.setState({
      isChecked: !this.state.isChecked
    });
    var num = e.target.value;
    this.props.onChange(num);
  }

  render() {
    const { isChecked } = this.state;

    if (isChecked === true) {
      var bgColor = "#f2355b";
    } else {
      bgColor = "#f7f7f7";
    }

    let disabled = this.props.disabled;

    if (this.props.numbers.includes(this.props.id)) {
      disabled = false;
    }

    return (
      <div className="number-container" id="checkboxes">
        <label
          className={!isChecked && disabled === false ? "num" : "checked-num"}
          style={{ backgroundColor: bgColor }}
        >
          {" "}
          {this.props.id}
          <input
            type="checkbox"
            name={this.props.id}
            value={this.props.id}
            id={this.props.id}
            onChange={this.handleChange}
            checked={isChecked}
            disabled={disabled}
          />
        </label>
      </div>
    );
  }
}

每当单击任何 Button 组件时,Parent 组件都会获取子 Button 的 id 值并将其放入其 numbers 状态数组中。每当取消选中 Button 时,通过删除子 Button 的 id,Parent 更新为 numbers 状态。

如果我的代码是正确的,预期的行为是每当单击Button 复选框时,父编号状态将立即更新(添加或删除数字)。但是,它总是落后一步更新。

我知道,问题在于 React 状态没有立即更新,我在 Stackoverflow 上检查了类似的问题。问题是我无法弄清楚如何使这两个组件以适当的方式相互交互。这个问题的解决方案是什么?

【问题讨论】:

  • setState 是异步的。您的 console.log 在传播状态更改之前执行。见stackoverflow.com/a/52690082/2477619
  • 您能详细说明一下吗?我知道这个问题,但是当我有两个组件时,我不明白如何解决它。

标签: reactjs


【解决方案1】:

这是来自代码和框的三个屏幕截图

如果你想玩它,请找到链接https://codesandbox.io/s/w2q8ypnxjw

我所做的是,我基本上复制并粘贴了您的代码并更新了 setNum 函数以反映 Think-Twice 建议的更改

  setNum(num) {
    if (!this.state.numbers.includes(num)) {
      this.setState(
        prevState => ({
          numbers: [...prevState.numbers, num]
        }),
        () => {
          console.log("state logged inside if", this.state.numbers);
        }
      );
    } else if (this.state.numbers.includes(num)) {
      let nums = [...this.state.numbers];
      let index = nums.indexOf(num);
      nums.splice(index, 1);
      this.setState({ numbers: nums }, () => {
        console.log("state logged inside else if", this.state.numbers);
      });
    }
    if (this.state.numbers.length >= 4) {
      this.setState({ disabled: true });
    } else if (this.state.numbers.length < 4) {
      this.setState({ disabled: false });
    }
  }

所以在继续之前,让我们快速解决一些关于 React 和 setState 的问题

  1. 正如B12Toaster 提到并提供了一个链接,其中包含 引用官方文档

    setState() 并不总是立即更新组件。它可能 批处理或推迟更新。

    Think-Twice 还指出,通过声明

    基本上 setState 在 React 中是异步的。当你修改一个值 使用 setState 您将只能在 渲染..

    所以如果你想看到一个地方的即时状态变化 您触发 setState,您可以使用回调函数作为 这样的setState(updater[, callback])

  2. 使用 setState 和 updater 有两种方法, 你可以传递一个对象,也可以传递一个函数 Think-Twice 的例子,一个对象作为更新器传递

    this.setState({ numbers: nums } //updater, () => {
            console.log(this.state.numbers); //this will print the updated value here
      });
    

    当一个函数被用作更新器时(在你的 setNum 函数中,你 已经这样做了),回调函数可以像下面这样使用

    if (!this.state.numbers.includes(num)) {
          this.setState(
            prevState => ({
              numbers: [...prevState.numbers, num]
            }),
            () => {
              console.log("state logged inside if", this.state.numbers);
            }
          );
        }
    

您当前的实现和通信结构似乎很好。它实际上被称为Lifting State Up,也是官方文档推荐的。 基本上,您将数组编号的状态存储在父组件中(这可以被视为事实的来源),然后将更改状态的方法作为道具传递给它的子组件。

在我提供的代码框链接中,功能按我期望的方式工作(至少这是我对您的代码的期望)

【讨论】:

    【解决方案2】:

    基本上 setState 在 React 中是异步的。当您使用 setState 修改值时,您将只能在渲染中看到更新的值。但是要立即查看更新的状态值,您需要执行以下操作

      this.setState({ numbers: nums }, () => {
            console.log(this.state.numbers); //this will print the updated value here
      });
    

    【讨论】:

    • 谢谢,但我无法获得正确更新 this.state.numbers 的预期结果。你能详细说明一下吗?
    • 感谢您的编辑,但我在 setNum 函数中有两个状态更改案例。如何用 prevState 改变一个?我对 React 有点陌生。
    • 对不起,我不明白你的问题。你能详细说明一下吗
    • 我想要的是能够在单击按钮时从状态数组中添加/删除数字。 console.log 只是查看状态的一种方式,它是次要的。我已经更新了问题文本以使其更加清晰。很抱歉造成混乱。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 1970-01-01
    • 2020-08-07
    • 2015-05-21
    相关资源
    最近更新 更多