Установщик хуков не устанавливает состояние с помощью объектной переменной - PullRequest
0 голосов
/ 21 июня 2020

Я пытаюсь создать простое древовидное меню с помощью react. Я нашел это видео , точно показывающее, чего я хочу достичь ( его Codepen ). Мне удалось реализовать его подход, но потом из любопытства попытался воспроизвести то же самое с помощью хуков. Я закончил с этим (упрощенно):

my Codepen

const App2 = () => { 
  const [selectedOptions, setSelectedOptions] = React.useState({});    

  React.useEffect(() => {
    console.log(selectedOptions);
  },[selectedOptions]);

  const updateSelection = (sel) => {
    console.log(sel)
    setSelectedOptions(sel);
  }

  return (
      <div className="wrapper">
        <h1>Toppings</h1>
        <OptionsList 
          options={options} 
          onChange={updateSelection}
          selectedOptions={selectedOptions} 
          />
      </div>
  );  
}

const OptionsList = ({ selectedOptions, onChange }) => { 
  const handleCheckboxClicked = (selectedOptionId) => {   
    if(selectedOptions[selectedOptionId]){
      delete selectedOptions[selectedOptionId];
    } else {
      selectedOptions[selectedOptionId] = {}      
    }    

    onChange(selectedOptions);
  }
  
  return (
    <div>
      <button onClick={() => (handleCheckboxClicked("chicken-id"))} >
        Set Chicken
      </button>      
    </div>
  )
}

ReactDOM.render(<App2 />, document.querySelector('#app'));

Проблема в setSelectedOptions(sel), внутри функции updateSelection ничего не делает (и конечно useEffect не запускается). Я не могу понять почему. Я помещаю console.log прямо над ним, чтобы проверить, в порядке ли переменная ("sel"), но вроде нормально. Я попытался жестко запрограммировать значение "sel", {курица-id: {}}, и оно работает, когда я это делаю.

1 Ответ

0 голосов
/ 21 июня 2020

Проблема в том, что вы напрямую мутируете объект состояния:

    if(selectedOptions[selectedOptionId]){
      delete selectedOptions[selectedOptionId];
    } else {
      selectedOptions[selectedOptionId] = {}      
    }    

    onChange(selectedOptions);

В обоих случаях вы изменяете объект состояния, а затем устанавливаете его как самого себя, поэтому, естественно, useEffect не будет срабатывать как установщик не внес никаких фактических изменений. Вы должны быть осторожны, делая это в React, так как это простой способ получить устаревшие значения. Повторная отрисовка запускается только тогда, когда есть разница между текущим состоянием и значением, переданным установщиком, а не всякий раз, когда значения состояния меняются по какой-либо причине.

В комментарии предлагается использовать распространение - ... - деструктурировать объект состояния, чтобы вы могли установить его как самого себя, но мне это кажется чем-то вроде анти-паттерна, тем более что он оставляет мутацию состояния на месте. Если вы хотите удалить запись из объекта состояния, общий подход будет заключаться в том, чтобы сначала клонировать объект, изменить клон, а затем установить это как новое состояние:

    const clone = Object.assign({}, selectedOptions);
    if(clone[selectedOptionId]){
      delete clone[selectedOptionId];
    } else {
      clone[selectedOptionId] = {}      
    };
    onChange(clone);

Однако небольшое предостережение, любые вложенные объекты внутри этого объекта сохранят свои ссылки на исходное состояние и все равно могут его видоизменить. Вам также придется клонировать эти вложенные объекты, чтобы избежать проблемы.

...