6

Как обновить вложенные свойства состояния в React

8

Я пытаюсь организовать состояние, используя вложенные свойства следующим образом:

this.state = {
   someProperty: {
      flag: true
   }
}

Однако обновление состояния таким образом:

this.setState({ someProperty.flag: false });

не работает. Как это можно сделать правильно?

5 ответ(ов)

1

Чтобы записать это в одну строку, используйте следующий код:

this.setState(prevState => ({ someProperty: { ...prevState.someProperty, flag: false } }));

Здесь используется функция обратного вызова prevState для обеспечения корректного обновления состояния.

1

Краткая версия:

Этот код

this.state = {
    someProperty: {
        flag: true
    }
}

следует упростить до чего-то подобного

this.state = {
    somePropertyFlag: true
}

Длинная версия:

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

Представим следующее состояние:

{
    parent: {
        child1: 'значение 1',
        child2: 'значение 2',
        ...
        child100: 'значение 100'
    }
}

Что произойдет, если вы измените только значение child1? React не перерисует представление, потому что использует поверхностное сравнение и не обнаружит изменений в свойстве parent. Кстати, изменение объекта состояния напрямую считается плохой практикой в общем случае.

Таким образом, вам нужно будет заново создать весь объект parent. Но в этом случае мы столкнемся с другой проблемой. React подумает, что все дочерние элементы изменили свои значения и перерисует все из них. Конечно, это неэффективно с точки зрения производительности.

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

0

Ваш код использует Object.assign для создания нового объекта состояния, но есть небольшая проблема, связанная с глубокой копией. При использовании Object.assign вы создаете поверхностную копию объекта this.state. Это означает, что вложенные объекты, такие как property и nestedProperty, все еще будут ссылаться на оригинальные значения в this.state.

В результате, если вы измените newState.property.nestedProperty, это может привести к нежелательным побочным эффектам, потому что property все еще будет ссылаться на тот же объект, что и ранее.

Чтобы избежать этой проблемы, вы можете использовать оператор распространения (spread operator) для создания глубокой копии. Вот как можно исправить код:

const newState = {
    ...this.state,
    property: {
        ...this.state.property,
        nestedProperty: "new value"
    }
};
this.setState(newState);

В этом примере мы создаем новый объект для property, а затем обновляем nestedProperty. Теперь изменения не будут затрагивать оригинальный объект state, и все будет работать корректно.

0

Если вы используете ES2015, у вас есть доступ к методу Object.assign. Вы можете использовать его следующим образом, чтобы обновить вложенный объект.

this.setState({
  someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});

Здесь вы объединяете обновлённые свойства с существующими и используете возвращаемый объект для обновления состояния.

Редакция: Я добавил пустой объект в качестве целевого аргумента для функции assign, чтобы убедиться, что состояние не мутируется напрямую, как указал carkod.

0

Вы можете использовать предложенное решение для работы с вложенным состоянием. Если у вас есть состояние, выглядит оно следующим образом:

this.state = {
  formInputs: {
    friendName: {
      value: '',
      isValid: false,
      errorMsg: ''
    },
    friendEmail: {
      value: '',
      isValid: false,
      errorMsg: ''
    }
  }
};

Вы можете определить функцию handleChange, которая будет копировать текущее состояние и переопределять его измененными значениями:

handleChange(el) {
  let inputName = el.target.name;
  let inputValue = el.target.value;

  let statusCopy = Object.assign({}, this.state);
  statusCopy.formInputs[inputName].value = inputValue;

  this.setState(statusCopy);
}

А вот пример HTML-кода с добавленным обработчиком событий:

<input type="text" onChange={this.handleChange} name="friendName" />

Таким образом, при изменении значения в поле ввода состояние будет обновляться корректно.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь