【问题标题】:Always get previous state in React app总是在 React 应用程序中获取先前的状态
【发布时间】:2018-05-18 11:55:24
【问题描述】:

关于我之前的question,我这样做是为了存储正确的状态:

if (value === 'checkpoint')
{
  if (checked1)
  {
    this.setState({checked1 : false})
    localStorage.setObject('checked1', false)
  }
  else
  {
    this.setState({checked1 : true})
    localStorage.setObject('checked1', true)
  }
}

现在我有多个复选框(假设我有四个)。一个用于所有复选框,另外 3 个用于类别。我希望我的应用程序检测三个类别复选框何时被选中,并自动选中 ALL 复选框。当任何类别复选框未选中时,ALL 复选框将自行取消选中。

我试过这段代码:

  if (checked1 && checked2 && checked3) {
    this.setState({checkedAll: true})
  } else {
    this.setState({checkedAll: false})
  }

但其他 3 个复选框 (checked1, checked2, checked3) 将始终获得以前的状态。

如何获得正确的状态以使我的checkedAll 正常工作?

【问题讨论】:

  • 当你说他们总是会得到以前的状态时,你是什么意思?另外,在您的第一个代码示例中:当 checkd1 为 true 时,为什么要将 check1 的状态设置为 false?
  • @ViktorG 这是切换。对于复选框,当您在 onChange() 上单击它时,您必须得到相反的值对吗?
  • 你能发布整个onChange处理程序吗?

标签: javascript html reactjs checkbox


【解决方案1】:

如果你使用 this.setState({new state}, //perform some action}),任何正在执行的动作都会收到最新的状态值。

复制并粘贴此组件并查看控制台以查看它正在接收正确的状态

import React, { Component } from 'react'

class Menu extends Component {
  state = {
    checkAll: false,
    check1: false,
    check2: false,
    check3: false
  }

  handleChange = () => {
    const all = (this.salmon.checked && this.tuna.checked && this.snapper.checked)
    if (all) {
      this.setState({
        checkAll: true,
        check1: this.salmon.checked,
        check2: this.tuna.checked,
        check3: this.snapper.checked
      }, () => {
        this.all.checked = true
        console.log('Most current state', JSON.stringify(this.state))
      })
    } else {
      this.setState({
        checkAll: false,
        check1: this.salmon.checked,
        check2: this.tuna.checked,
        check3: this.snapper.checked
      }, () => {
        this.all.checked = false
        console.log('Most current state', JSON.stringify(this.state))
      })
    }
  }

  handleAll = () => {
    this.setState({
      checkAll: true,
      check1: true,
      check2: true,
      check3: true
    }, () => {
      this.salmon.checked = true
      this.tuna.checked = true
      this.snapper.checked = true
      console.log('Most current state', JSON.stringify(this.state))
    })
  }

  render() {
    return (
      <div>
        <h1>Select Options</h1>
        <form>
          <span>
            Select All:
            <input
              onChange={this.handleAll}
              type="checkbox"
              value={this.state.checkAll}
              ref={all => this.all = all}
            />
          </span>
          <ul>
            <li>
              <input
                onChange={this.handleChange}
                type="checkbox"
                value={this.state.check1}
                ref={salmon => this.salmon = salmon}
              />
              Salmon
            </li>
            <li>
              <input
                onChange={this.handleChange}
                type="checkbox"
                value={this.state.check2}
                ref={tuna => this.tuna = tuna}
              />
              Tuna
            </li>
            <li>
              <input
                onChange={this.handleChange}
                type="checkbox"
                value={this.state.check3}
                ref={snapper => this.snapper = snapper}
              />
              Snapper
            </li>
          </ul>
        </form>
      </div>
    )
  }
}

export default Menu

【讨论】:

  • 但对我来说不是回调的问题。这是检测的问题。我需要检测是否选中了所有 3 个类别复选框。当检测使用之前的状态时,我的checkedAll不会正确。
  • 我将在上面更新我的答案,您应该能够将组件复制并粘贴到您的代码中。由于callback 发生在实际设置状态之后,您将收到您正在寻找的值而不是之前的状态。
  • 至于检测refs有助于获取DOM值。请注意,将 console.log 语句放置在回调中时实际上返回当前状态,这消除了创建附加变量的需要(引用您的原始问题)。希望上面的代码有所帮助。
【解决方案2】:

您应该避免在componentDidUpdate() 中使用setState(),因为它有时会导致代码中的错误并且被认为不是一个好的做法(例如https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md,因此如果您使用airbnb 规则来配置您的eslint,您还将遇到一些 linting 警告)。

你不能让所有四个复选框都像这样控制输入吗:

      <input
        type="checkbox"
        id="ch1"
        value="1"
        checked={this.state.ch1}
        onChange={this.onCheckboxChange}
      /> Ch1

      <input
        type="checkbox"
        id="ch2"
        value="1"
        checked={this.state.ch2}
        onChange={this.onCheckboxChange}
      /> Ch2

      <input
        type="checkbox"
        id="ch3"
        value="1"
        checked={this.state.ch3}
        onChange={this.onCheckboxChange}
      /> Ch3

      <input
        type="checkbox"
        id="chall"
        value="1"
        checked={
          this.state.ch1
          && this.state.ch2
          && this.state.ch3
        }
        onChange={this.onCheckboxChange}
      /> Ch all

然后在onCheckboxChange(或任何名称)中执行以下操作:

const { id } = e.target;

if (id === 'chall') {
  if (e.target.checked) {
    this.setState({
      ch1: true,
      ch2: true,
      ch3: true,
    });
    return;
  }

  this.setState({
    ch1: false,
    ch2: false,
    ch3: false,
  });
}

this.setState({
  [id]: e.target.checked,
});

【讨论】:

  • @ThomasRychtyk 我没有将 setstate 放在 componentdidupdate 中。我把它放在我点击复选框后运行的 onChange() 函数中。就像你的答案一样。
  • 我认为他是在建议您不要使用我的答案,这很公平,尽管我认为不需要在 componentDidUpdate 中针对 setState 制定一揽子规则,只要您用现有状态覆盖它像我一样检查。尽管如此,它仍会触发额外的渲染,因此使用受控组件绝对有意义。
【解决方案3】:

我认为更重要的是您在生命周期中放置此代码的位置。 componentDidUpdate 似乎是一个合理的候选人。例如:

componentDidUpdate () {
  const { checked1, checked2, checked3 } = this.state
  const checkedAll = checked1 && checked2 && checked3
  if (checkedAll === this.state.checkedAll) {
    return
  }
  this.setState({ checkedAll })
}

换句话说,一旦盒子都更新了它们的状态,checkAll 自然也会更新。

【讨论】:

  • @RichChrucher 我将代码放在 onChange() 函数中,用户按下复选框后将执行该函数
  • 是的,更新各个复选框的状态很好。但是对于checkedAll,我建议执行上述操作,这样它会(最终)发生在您的其他状态更改之后
猜你喜欢
  • 1970-01-01
  • 2018-02-16
  • 2021-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-30
  • 1970-01-01
相关资源
最近更新 更多