反应复选框。切换复选框时状态晚

时间:2020-04-23 23:51:18

标签: reactjs checkbox react-redux state react-state

我有一个由3个复选框组成的组,和一个用于选中这3个复选框的主复选框。

  • 选中所有3个复选框后,主要复选框将被选中。
  • 当我选中这三个复选框时,什么都没有发生,但是当我然后取消选中其中一棵树时,复选框就被选中了。

有人可以向我解释幕后实际上发生了什么,并以某种方式帮助我解决React状态的这个奥秘吗?谢谢!

以下是代码片段:

state = {
  data: [
    { checked: false, id: 1 },
    { checked: false, id: 2 },
    { checked: false, id: 3 }
  ],
  main: false,
}

onCheckboxChange = id => {
  const data = [...this.state.data];

  data.forEach(item => {
    if (item.id === id) {
      item.checked = !item.checked;
    }
  })

  const everyCheckBoxIsTrue = checkbox.every(item => item === true);

  this.setState({ data: data, main: everyCheckBoxIsTrue });
}

onMainCheckBoxChange = () => {
  let data = [...this.state.data];

  data.forEach(item => {
    !this.state.main ? item.checked = true : item.checked = false
  })

  this.setState({
    this.state.main: !this.state.main,
    this.state.data: data,
  });
}

render () {
  const checkbox = this.state.data.map(item => (
      <input
        type="checkbox"
        checked={item.checked}
        onChange={() => this.onCheckboxChange(item.id)}
      />
    ))
  }

return (
  <input type="checkbox" name="main" checked={this.state.main} onChange={this.onMainCheckBoxChange} />
  {checkbox}
)

2 个答案:

答案 0 :(得分:1)

我无法根据您提供的代码创建有效的代码段,其中一个问题是:

const everyCheckBoxIsTrue = checkbox.every(item => item === true);

未定义checkbox的地方。

但是,我认为您对于使用旧状态还是新状态感到困惑,如果您清楚地命名,例如:

eventHandler() {
  const { data } = this.state; // old state

  const newData = data.map(each => ...); // new object, soon-to-be new state

  this.setState({ data }); // update state
}

这是一个工作示例供您参考:

class App extends React.Component {
  state = {
    data: [
      { checked: false, id: 1 },
      { checked: false, id: 2 },
      { checked: false, id: 3 }
    ],
    main: false,
  }
  
  onCheckboxChange(id) {
    const { data } = this.state;
    
    const newData = data.map(each => {
      if (each.id === id) {
        // Toggle the previous checked value
        return Object.assign({}, each, { checked: !each.checked });
      }
      return each;
    });
    
    this.setState({
      data: newData,
      // Check if every checked box is checked
      main: newData.every(item => item.checked === true),
    });
  }
  
  onMainCheckBoxChange() {
    const { main, data } = this.state;
     
    // Toggle the previous main value
    const newValue = !main;

    this.setState({
      data: data.map(each => Object.assign({}, each, { checked: newValue })),
      main: newValue,
    });
  }
  
  render () {
    const { data, main } = this.state;

    return (
      <div>
        <label>Main</label>
        <input
          type="checkbox"
          name="main"
          // TODO this should be automatically checked instead of assigning to the state
          checked={main}
          onChange={() => this.onMainCheckBoxChange()}
        />
        {
          data.map(item => (
            <div>
              <label>{item.id}</label>
              <input
                type="checkbox"
                checked={item.checked}
                onChange={() => this.onCheckboxChange(item.id)}
              />
            </div>
          ))
        }
      </div>
    );
  }
}

ReactDOM.render(
  <App />
, document.querySelector('#app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="app"></div>

旁注:您可能要考虑不使用main状态

答案 1 :(得分:0)

您不应该存储state.main来确定是否选中了每个复选框。

您已经存储了确定是否选中所有复选框的状态,因为如果state.data中的每个对象都具有checked: true,则必须选中所有复选框。

您可以像这样简单地渲染主复选框:

<input
  type="checkbox"
  name="main"
  checked={this.state.data.every(v => v.checked)}
  onChange={this.onMainCheckBoxChange}
/>;

如果选中所有复选框,则this.state.data.every(v => v.checked)行将返回true

并且当切换主复选框时,该函数可以如下所示:

onMainCheckBoxChange = () => {
  this.setState(prev => {
    // If all are checked, then we want to uncheck all checkboxes
    if (this.state.data.every(v => v.checked)) {
      return {
        data: prev.data.map(v => ({ ...v, checked: false })),
      };
    }

    // Else some checkboxes must be unchecked, so we check them all
    return {
      data: prev.data.map(v => ({ ...v, checked: true })),
    };
  });
};

优良作法是只存储需要存储的状态。可以从其他状态计算出的任何状态(例如,“是否都选中了所有复选框?”)应在render函数内部进行计算。 See here说:

什么不应该进入州? ...计算数据:不必担心基于状态的预计算值-如果在render()中进行所有计算,则更容易确保UI一致。例如,如果您有一个处于状态的列表项数组,并且想要将计数呈现为字符串,则只需在render()方法中呈现this.state.listItems.length +'list items'而不是将其存储在状态中