#javascript #arrays #reactjs #ecmascript-6 #immutability
#javascript #массивы #reactjs #ecmascript-6 #неизменность
Вопрос:
Этот вопрос может немного отличаться от вопроса «наилучшей практики», но, пожалуйста, потерпите меня.
Вот часть моего состояния:
this.state = {
typeElements: {
headers: [
{
name: "h1",
size: 70,
lineHeight: 1.25,
kearning: 0,
marginAfter: 0
}, {
name: "h2",
size: 70,
lineHeight: 1.25,
kearning: 0,
marginAfter: 0
}, {
name: "h3",
size: 70,
lineHeight: 1.25,
kearning: 0,
marginAfter: 0
}...
Что мне нужно сделать, это ЗАМЕНИТЬ объект с заданным индексом в массиве заголовков.
Я не знаю, как это сделать с помощью метода setState, как в this.setState(headers[1] = {obj})
— но это, очевидно, недопустимо. Мой текущий метод создает новый массив и удаляет старый следующим образом:
_updateStyle(props) {
let newState = Object.assign({}, this.state)
newState.typeElements.headers[props.index] = props
this.setState(newState)
};
Для моего небольшого хакерского проекта, я думаю, все в порядке, но я чувствую, что это очень сложно и быстро приведет к проблемам с производительностью в любом масштабе.
Ответ №1:
Обновлено: поскольку этот ответ по-прежнему получает голоса, имейте в виду, что предыдущий ответ ниже устарел для современных JavaScript и React. Аддон «обновление» теперь является устаревшим, и вместо него можно использовать «помощник неизменности».
В документах React также упоминается, почему важна неизменность, поэтому избегайте изменения состояния. Для неизменяемых обновлений вы можете использовать Object.assign()
или распространять синтаксис, который необходимо выполнить для каждого уровня вложенности, как в этом примере вложенный headers
объект и его элементы массива. В этом конкретном примере мы можем использовать индекс массива в качестве ключа, поэтому можно также использовать оператор spread для создания мелкого клонирования массива и присвоения нового объекта в качестве значения по заданному индексу в клонированном массиве.
_updateStyle (props) {
const { typeElements } = this.state;
const updatedHeaders = [...typeElements.headers];
updatedHeaders[props.index] = props;
this.setState({
...this.state,
typeElements: {
...typeElements,
headers: updatedHeaders
}
));
}
Другое решение, которое не требует расширенного синтаксиса и необходимо, если мы не используем индекс массива для поиска объекта, который мы хотим заменить, — это использование array.map
для создания нового массива и возврата нового объекта вместо старого с заданным индексом.
const updatedHeaders = typeElements.headers.map((obj, index) => {
return index === props.index ? props : obj;
});
Аналогичные примеры в документах Redux также объясняют «неизменяемые шаблоны обновления».
Для этого у React есть несколько помощников неизменности, что объясняется в документах: https://facebook.github.io/react/docs/update.html
В вашем случае вы могли бы использовать команду $splice, чтобы удалить один элемент и добавить новый с заданным индексом, например:
_updateStyle (props) { this.setState(update(this.state.typeElements, { $splice: [[props.index, 1, props]] } )); }
Комментарии:
1. ага, я знал об этом, но не был уверен, как это реализовать, так что это происходит в методе setState? Что делает «1»? Это точка вставки нового объекта? Не мог бы я просто снова использовать props.index для этого, если это так?
2. Проверьте метод array.splice() developer.mozilla.org/nl/docs/Web/JavaScript/Reference /… и ты поймешь… массив содержит аргументы, переданные методу splice, поэтому «1» в этом примере является значением deleteCount .
Ответ №2:
Предлагая лучшее объяснение того, как это сделать.
- Сначала найдите индекс элемента, который вы заменяете, в массиве состояний.
- Во-вторых,
update
элемент с этим индексом - В-третьих, вызовите
setState
новую коллекцию
import update from 'immutability-helper';
// this.state = { employees: [{id: 1, name: 'Obama'}, {id: 2, name: 'Trump'}] }
updateEmployee(employee) {
const index = this.state.employees.findIndex((emp) => emp.id === employee.id);
const updatedEmployees = update(this.state.employees, {$splice: [[index, 1, employee]]}); // array.splice(start, deleteCount, item1)
this.setState({employees: updatedEmployees});
}
Ответ №3:
используйте помощник неизменности
вы можете найти там хорошие примеры
Ответ №4:
Объект.Assign использует мелкую копию, а не глубокую копию.
Пожалуйста, имейте в виду, что в вашем примере Object.assign({}, this.state)
копируются только ссылки на ближайшие дочерние элементы state
, т. Е. На typeElements
, Но headers
массив не копируется.
В ES6 есть синтаксический сахар ...
для объекта.Назначьте, у Babel есть специальный аддон transform-object-rest-spread .
let newHeaders = { ...this.state.typeElements.headers};
newHeaders[index] = props.Something;
let newState = {...state, typeElements: { newHeaders }};
Комментарии:
1. Действительно? На самом деле он тоже скопировал заголовки для меня. Можете ли вы уточнить?
2. @motleydev Метод Object.assign() копирует только перечислимые и собственные свойства из исходного объекта в целевой объект (MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference /… ). Это означает, что он копирует только ссылки на ближайшие объекты или значения для примитивов (чисел, строк). Вы получаете старый массив в своей реализации, и если вы его измените, это также повлияет на другие места, где он используется.