Правильно ли так изменять state?

176
08 августа 2021, 17:10

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

Я это сделал так:

completeClick(e) {
    const id = e.target.id;
    let result = this.state.tasksArray.find(item => item.id == id);
    result.span = true;
    this.setState({ state: 1 }); 
}

Но из за того что я не могу изменить свойство напрямую(this.setState()), я изменяю его через костыль result.span = true . Как изменить код так , что бы я могу найти объект и изменить его напрямую через setState?

Answer 1

Документация по React гласит (и я полностью с ней согласен):

Никогда не мутируйте this.state напрямую, так как более поздний вызов setState() может перезаписать эту мутацию. Относитесь к this.state как к иммутабельному объекту.

Мутация состояния state приводит к непредвиденному поведению, так называемые сайд эффекты. Поэтому предлагаю Вам следующий способ изменения элемента массива из состояния:

completeClick(e) {
    const id = e.target.id;
    // ищем индекс элемента в массиве, удовлетворяющий условию поиска
    const index = this.state.tasksArray.findIndex(item => item.id == id);
    // если элемент был найден - выполним следующий код
    if (index != -1) {
      this.setState(prevState => ({
        tasksArray: {
          ...prevState.tasksArray,
          [prevState.tasksArray[index].span]: true,
        },
      }));
    }
}

Такой подход гарантирует нам, что состояние не будет мутировать. Полезные ссылки:

  • React: How do I update state.item[i] on setState?
  • Correct modification of state arrays in ReactJS
  • Spread syntax
  • Array.prototype.findIndex
  • Ошибка с пушем обьетов в массив, как пушить в массив state?

Если данный подход выглядит громоздким или сложным, то можно попробовать сделать это через метод Object.assign.

Метод Object.assign() используется для копирования значений всех собственных перечисляемых свойств из одного или более исходных объектов в целевой объект. После копирования он возвращает целевой объект.

В Вашем случае должно получиться как-то так:

completeClick(e) {
    const id = e.target.id;
    // клонирование исходного массив из state (можно и через spread syntax)
    const tasks = Object.assign({}, this.state.tasksArray)
    // ищем индекс элемента в массиве, удовлетворяющий условию поиска
    const index = this.state.tasksArray.findIndex(item => item.id == id);
    // если элемент был найден - выполним следующий код
    if (index != -1) {
      // меняем значение найденного элемента
      tasks[index].span = true;
      // присваиваем новое значение, без мутации
      this.setState({ tasksArray: tasks });
    }      
}

Но есть обратная сторона медали, если объект имеет сложную вложенную структуру, метод Object.assign() может начать работать не так, как хотелось бы. Для решения подобных вопросов можно воспользоваться готовым методом из lodash библиотеки: _.cloneDeep(value) - этот метод рекурсивно клонирует значения.

READ ALSO
Redux вопрос архитектуры

Redux вопрос архитектуры

Я осваиваю веб разработку и у меня созрел вопрос:

105
Выпадающее меню vue.js

Выпадающее меню vue.js

Нужна помощь в реализации выпадающего меню в vuejs

203
Консоль выдает ошибку Uncaught SyntaxError: Unexpected token )

Консоль выдает ошибку Uncaught SyntaxError: Unexpected token )

Идея в том , что бы код считал сколько есть слов в массиве , которые заканчиваются "у" "іе"

218
Не устанавливается флажок checked (radio button) в onChange

Не устанавливается флажок checked (radio button) в onChange

Я в React JS новый, хотел бы кое что уточнить Код есть, в базе данных все корректно меняется при нажатии на кнопкуПроблема лишь в том, что оно не check-ается...

180