Реагируйте флажками. Состояние поздно при переключении флажков - PullRequest
2 голосов
/ 24 апреля 2020

У меня есть группа из 3 флажков и основной флажок для проверки этих 3 флажков.

  • Когда я выбираю все 3 флажка, я хочу, чтобы флажок main стал отмеченным.
  • Когда я проверяю эти 3 флажка, ничего не происходит, но когда я снимаю флажок с одного из этих деревьев, флажок main становится установленным.

Может кто-нибудь объяснить мне, что на самом деле происходит за кулисами и помочь мне как-то разгадать эту загадку состояния 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 ]

1 голос
/ 24 апреля 2020

Я не могу сделать рабочий фрагмент кода на основе предоставленного вами кода, одна из проблем была:

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

0 голосов
/ 24 апреля 2020

Вы не должны хранить 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. Смотрите здесь , где написано:

Что не должно Go в штате? ... Вычисленные данные: не беспокойтесь о предварительных вычислениях значений, основанных на состоянии - легче обеспечить согласованность вашего пользовательского интерфейса, если вы выполняете все вычисления в render (). Например, если у вас есть массив элементов списка в состоянии и вы хотите отобразить счетчик в виде строки, просто отобразите this.state.listItems.length + 'list items' в вашем методе render (), а не храните его в состоянии .

...