Неизменяемость состояния — одна из ключевых тем в React. Её легко придерживаться, работая с примитивными типами данных, но с составными, такими как объекты и массивы, у неподготовленного пользователя могут возникнуть сложности. В этом уроке рассматриваются основные способы частичного обновления объектов и массивов.
Кроме примеров на чистом JS, будут продемонстрированы примеры с использованием библиотеки immutability-helper, которая создана для облегчения выполнения подобных операций. Она особенно актуальна при выполнении обновлений там, где код на JS получается слишком сложным.
Массивы
Массив: добавление
Самое простое — это добавление в массив:
const items = ['one', 'two', 'three'];
const item = 'four';
const newItems = [...items, item];
// ['one', 'two', 'three', 'four'];
Если необходимо добавить элемент в начало, то нужно всего лишь поменять местами элементы массива:
const newItems = [item, ...items];
// ['four', 'one', 'two', 'three'];
Использование immutability-helper
import update from 'immutability-helper';
const state1 = ['x'];
const state2 = update(state1, { $push: ['y'] }); // ['x', 'y']
Массив: удаление
Более интересный пример. Чтобы успешно выполнить удаление, нужно знать, что удалять. Это значит, что каждый элемент в коллекции должен иметь идентификатор. Для удаления используется старая добрая фильтрация.
const newItems = items.filter((item) => item.id !== id);
Может возникнуть вопрос: откуда взялся идентификатор внутри обработчика? И здесь нам на помощь приходят замыкания.
See the Pen js_react_immutability_array_remove_element by Hexlet (@hexlet) on CodePen.
Обратите внимание на способ задания обработчика: removeItem = (id) => (e) => {
и его использование onClick={this.removeItem(id)}
.
Использование immutability-helper
const index = 5;
const newItems = update(items, {$splice: [[index, 1]]});
Удаление на чистом JS через фильтр — самый оптимальный способ. С использованием immutability-helper
получается сложно.
Массив: изменение
К сожалению, без дополнительных инструментов код решения будет слишком громоздким. Он приведён для ознакомления, но в реальном коде так делать не надо.
const index = items.findIndex((item) => item.id === id);
const newItem = { ...items[index], value: 'another value' };
const newItems = [...items.slice(0, index), newItem, ...items.slice(index + 1)];
Думаю, не придётся вас убеждать в том, что это перебор :)
Использование immutability-helper
const collection = { children: ['zero', 'one', 'two'] };
const index = 1;
const newCollection = update(collection, { children: { [index]: { $set: 1 } } });
// { children: ['zero', 1, 'two'] }
Как видно, этот способ значительно проще и чище. Рекомендуется к использованию.
Объекты
Объект: добавление
Так же просто, как и с массивом.
const items = { a: 1, b: 2 };
const newItems = { ...items, c: 3 };
// { a: 1, b: 2, c: 3 }
Либо, если ключ вычисляется динамически, нужно делать так:
const items = { a: 1, b: 2 };
const key = 'c';
const newItems = { ...items, [key]: 3 };
// { a: 1, b: 2, c: 3 }
Объект: удаление
На помощь приходит деструктуризация:
const { deletedKey, ...newState } = state;
Использование immutability-helper
import update from 'immutability-helper';
const state = { a: 1, c: 3 };
const updatedState = update(state, {
$unset: ['c'],
});
// { a: 1 }
Объект: изменение
Абсолютно то же самое, что и добавление.
const items = { a: 1, b: 2 };
const newItems = { ...items, a: 3 };
// { a: 3, b: 2 }
Использование immutability-helper
const data = { a: 1, b: 2 };
const key = 'a';
const newData = update(data, { [key]: { $set: 3 } });
// { a: 3, b: 2 }
Глубокая вложенность
В примерах выше в основном можно обходиться стандартными средствами JS, и только в некоторых ситуациях удобнее пользоваться сторонними решениями. В реальном коде всё будет также, особенно если учитывать рекомендацию React и держать состояние максимально плоским. Но в некоторых ситуациях данные, которые нужно изменить, находятся не на поверхности, а в глубине структур. К сожалению, в этих ситуациях обычный JS-код будет раздуваться. И тут уже точно не обойтись без дополнительных библиотек.
import update from 'immutability-helper';
const myData = {
x: { y: { z: 5 } },
a: { b: [1, 2] },
};
const newData = update(myData, {
x: { y: { z: { $set: 7 } } },
a: { b: { $push: [9] } }
});
console.log(newData)
// => { x: { y: { z: 7 } }, a: { b: [ 1, 2, 9 ] } }
https://repl.it/@hexlet/js-react-immutability-helper
Аналоги
immutability-helper
— не единственная библиотека для подобных задач. Вот еще несколько популярных:
- immutable-js — основана на персистентных данных
- updeep — активно использует каррирование
- immerjs — пожалуй, самая популярная библиотека в JS для работы с неизменяемыми данными
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.