Зарегистрируйтесь, чтобы продолжить обучение

Неизменяемость JS: React

Неизменяемость состояния — одна из ключевых тем в 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 для работы с неизменяемыми данными

Дополнительные материалы

  1. Updating Arrays in State
  2. Updating Objects in State

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
Верстка на HTML5 и CSS3, Программирование на JavaScript в браузере, разработка клиентских приложений используя React
10 месяцев
с нуля
Старт 26 декабря
профессия
Программирование на JavaScript в браузере и на сервере (Node.js), разработка бекендов на Fastify и фронтенда на React
16 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»