Как обновить вложенные свойства состояния в React
Я пытаюсь организовать состояние, используя вложенные свойства следующим образом:
this.state = {
someProperty: {
flag: true
}
}
Однако обновление состояния таким образом:
this.setState({ someProperty.flag: false });
не работает. Как это можно сделать правильно?
5 ответ(ов)
Чтобы записать это в одну строку, используйте следующий код:
this.setState(prevState => ({ someProperty: { ...prevState.someProperty, flag: false } }));
Здесь используется функция обратного вызова prevState
для обеспечения корректного обновления состояния.
Краткая версия:
Этот код
this.state = {
someProperty: {
flag: true
}
}
следует упростить до чего-то подобного
this.state = {
somePropertyFlag: true
}
Длинная версия:
В настоящее время не стоит использовать вложенное состояние в React. Дело в том, что React не предназначен для работы с вложенными состояниями, и все предложенные решения на этот счет выглядят как обходные пути. Они не используют возможности фреймворка, а скорее, борются с ним. Такие подходы приводят к написанию кода, который не так прозрачен, а цель сгруппировать некоторые свойства вызывает сомнения. Поэтому хотя они и интересны как ответ на задачу, на практике они мало полезны.
Представим следующее состояние:
{
parent: {
child1: 'значение 1',
child2: 'значение 2',
...
child100: 'значение 100'
}
}
Что произойдет, если вы измените только значение child1
? React не перерисует представление, потому что использует поверхностное сравнение и не обнаружит изменений в свойстве parent
. Кстати, изменение объекта состояния напрямую считается плохой практикой в общем случае.
Таким образом, вам нужно будет заново создать весь объект parent
. Но в этом случае мы столкнемся с другой проблемой. React подумает, что все дочерние элементы изменили свои значения и перерисует все из них. Конечно, это неэффективно с точки зрения производительности.
Эту проблему все же можно решить, написав довольно сложную логику в shouldComponentUpdate()
, но я предпочел бы остановиться здесь и использовать простое решение из краткой версии.
Ваш код использует 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
, и все будет работать корректно.
Если вы используете ES2015, у вас есть доступ к методу Object.assign
. Вы можете использовать его следующим образом, чтобы обновить вложенный объект.
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
Здесь вы объединяете обновлённые свойства с существующими и используете возвращаемый объект для обновления состояния.
Редакция: Я добавил пустой объект в качестве целевого аргумента для функции assign
, чтобы убедиться, что состояние не мутируется напрямую, как указал carkod.
Вы можете использовать предложенное решение для работы с вложенным состоянием. Если у вас есть состояние, выглядит оно следующим образом:
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" />
Таким образом, при изменении значения в поле ввода состояние будет обновляться корректно.
Как условно добавлять атрибуты к компонентам React?
В чем разница между React Native и React?
Когда использовать JSX.Element, ReactNode и ReactElement?
Как реализовать дебаунс?
Обнаружена ошибка: Невозможное нарушение: Неверный тип элемента: ожидался строковый тип (для встроенных компонентов) или класс/функция, но получен объект