Если видео недоступно для просмотра, попробуйте выключить блокировщик рекламы.

Неизменяемость состояния - одна из ключевых тем в Реакте. Ее легко придерживаться, работая с примитивными типами данных, но с составными, такими как объекты и массивы, у неподготовленного пользователя могут возникнуть сложности. В этом уроке мы пробежимся по основным способам частичного обновления объектов и массивов.

Кроме примеров на чистом 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 через фильтр - самый оптимальный способ. С использованием immutabiliy-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 }

Объект: удаление

Решение ниже привожу только для ознакомления. На чистом js нет простого способа удалить ключ в неизменяемом стиле:

Object.keys(myObj)
  .filter(key => key !== deleteKey)
  .reduce((acc, current) => ({ ...acc, [current]: myObj[current] }), {});
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, и только в некоторых ситуациях удобнее пользоваться сторонними решениями. В реальном коде все будет также, особенно если учитывать рекомендацию Реакта и держать свой стейт максимально плоским. Но в некоторых ситуациях данные, которые нужно изменить, находятся не на поверхности, а в глубине структур. К сожалению, в этих ситуациях обычный js код будет раздуваться. И тут уже точно не обойтись без дополнительных библиотек.

Аналоги

immutability-helper — не единственная библиотека для подобных задач. Вот еще несколько популярных:

  • immutable-js - основана на персистентных данных;
  • updeep - активно использует каррирование.
Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Node, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →