反应复选框。切换复选框时状态迟到

Posted

技术标签:

【中文标题】反应复选框。切换复选框时状态迟到【英文标题】:React checkboxes. State is late when toggling the checkboxes 【发布时间】:2020-08-07 11:07:00 【问题描述】:

我有一组 3 个复选框和用于检查这 3 个复选框的主复选框。

当我选中所有 3 个复选框时,我希望 ma​​in 复选框被选中。 当我选中这 3 个复选框时,没有任何反应,但是当我取消选中其中一棵树时,ma​​in 复选框变为选中状态。

有人可以向我解释幕后实际发生的事情并帮助我以某种方式解决这个 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
)

【问题讨论】:

【参考方案1】:

我无法根据您提供的代码制作工作代码 sn-p,其中一个问题是:

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 状态

【讨论】:

非常感谢。我还有一个问题。为什么要用括号括起来 data 变量? // 常量 data = this.state @Rakonjac 不用担心!啊,这是对象解构,所以const data = this.state;const data = this.state.data; 相同,因为this.state 是一个对象。如果您想从同一个对象访问多个,这很有用,例如:const data, main = this.state; 而不是const data = this.state.data; const main = this.state.main;,因为每次调用this.state.xxx,您的程序都会访问内存,因此在这种情况下使用解构将最小化(也更容易阅读代码)。我建议你使用 ESLint 来学习好的实践:) 听起来不错。再次感谢,您的解释很有帮助。【参考方案2】:

您不应该存储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 =&gt; 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' 而不是将其存储在状态中.

【讨论】:

以上是关于反应复选框。切换复选框时状态迟到的主要内容,如果未能解决你的问题,请参考以下文章

反应复选框不更新

未选中特定复选框时,反应切换选择字段未删除

Angular 反应复选框仅在第一次单击后正确切换

点击时复选框 UIButton 标题不切换

如何在反应表中使用另一个不确定的复选框列

多个复选框过滤反应状态