Создает ли React глубокую или поверхностную копию prevState для внутреннего использования? - PullRequest
1 голос
/ 10 апреля 2020

Я только начинаю с программирования и недавно написал свое первое приложение на React, и хотя оно работает, я не уверен, правильно ли я обрабатываю свое внутреннее состояние. Мой вопрос заключается в том, создает ли метод setState неизменяемую глубокую копию «prevState» или не обязательно? Чтобы показать пример:

 menuAddRecHandler = () => {
    this.setState((prevState) => {
      const updatedState = {...prevState};
      const dayInd = updatedState.menu.recipes.findIndex(item => item.dayNum === updatedState.menuInternal.currentDay);
      updatedState.menu.recipes[dayInd].recList.push({
        name: prevState.menuInternal.menuRecSelect,
        portions: prevState.menuInternal.menuNumPortInput
      });
      return updatedState;
    })
  }

В моем обработчике я помещаю объект во вложенный массив объекта updatedState, который был скопирован из prevState оператором распространения. Теперь я знаю, что оператор распространения делает только поверхностную копию, но является ли prevState внутренне предоставленным методом setState также поверхностным, и означает ли это, что я фактически изменяю свое состояние напрямую, вызывая этот обработчик? Если так, как я могу решить эту проблему? Спасибо.

Ответы [ 3 ]

3 голосов
/ 10 апреля 2020

Из документов :

Первый аргумент - это функция обновления с подписью:

(state, props) => stateChange

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

Как добиться того же результата без изменения?

menuAddRecHandler = () => {
  this.setState((prevState) => {
    return {
      ...prevState,
      menu: {
        ...prevState.menu,
        recipes: prevState.menu.recipes.map((item) => {
          if(item.dayNum === prevState.menuInternal.currentDay) {
            return {
              ...item,
              recList: [
                ...item.recList,
                {
                  name: prevState.menuInternal.menuRecSelect,
                  portions: prevState.menuInternal.menuNumPortInput
                }
              ]
            }
          }
          return item;
        })
      }
    }
  });
}

Это очевидно, очень подвержен ошибкам, идея состоит в том, чтобы иметь более глубокие состояния. Документы для Redux предлагают несколько советов по нормализации состояния .

. Вы также можете посмотреть библиотеки неизменяемых состояний, такие как Immutable JS или Immer

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

В вашем примере много "шума", поэтому я сделаю это просто:

state = {  name: `Dennis`, age: 89 }

this.setState((prevState) => {
  return { age: prevState.age + 1 }; 
}); // { name: `Dennis`, age: 90 }

this.setState({ name: `John` }); // { name: `John`, age: 89 }

В компоненте класса this.setState сливается поверхностно с предыдущим состоянием, а prevState является действительной ссылкой на состояние, т. Е. { name: "Dennis", age: 89 }.

Состояние и реквизиты, полученные функцией средства обновления, гарантированно будут актуальными. , Вывод средства обновления поверхностно объединен с состоянием.

Обратите внимание, что это не случай с функциональными компонентами (распространенная ошибка).

В отличие от метода setState, найденного в компонентах класса, useState не объединяет объекты обновления автоматически.

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

Согласно документам это не копия вообще

состояние является ссылкой на состояние компонента во время применения изменения. Он не должен быть напрямую мутирован.

Создание глубокой копии обычно ухудшает производительность, но если вам нужно это сделать, вы можете сделать что-то вроде этого:

const updatedState = JSON.parse(JSON.stringify(prevState));
...