【问题标题】: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}
)

【问题讨论】:

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


    【解决方案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' 而不是将其存储在状态中.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-27
      • 2020-06-13
      • 2015-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多