Вам часто говорят не изменять вещи в приложениях React. Это действительно хороший совет, но не всегда понятно, как на самом деле выполнять эти обновления. Давайте проясним это.

Типы данных

Прежде чем мы сможем говорить о неизменяемых обновлениях, важно понять, с какими значениями мы работаем. Не используйте пользовательские классы!

Почему? Не существует единого способа клонирования экземпляров пользовательских классов в JavaScript. Стандартные методы неизменяемых обновлений приведут к потере класса, включая методы прототипа.

Вы обнаружите, что большинство проектов React используют простые объекты, массивы и примитивы. Это имеет дополнительное преимущество, заключающееся в том, что вы можете переходить в/из JSON без дополнительного кода.

Одним заметным исключением является Immutable.js, в котором есть специальные классы, предназначенные для неизменяемых обновлений — это нормально.

Глубокое клонирование

Как обновить объект, не изменяя оригинал? Простой ответ: вы просто глубоко клонируете весь объект, а затем мутируете его. Проблема в том, что мы отказываемся от одного из преимуществ неизменности: простой проверки того, что объект не был обновлен с помощью ===.

Эта проверка используется в таких вещах, как React.PureComponent и connect в react-redux. Отказ от клонирования неизмененных объектов может существенно повлиять на производительность.

Обратите внимание, что поверхностные клоны — это хорошо, а вот глубоких клонов нам следует избегать.

Массивы

Массивы довольно легко клонировать. У них есть метод slice, который при вызове с нулевыми аргументами возвращает копию массива.

Небольшое замечание: на самом деле мы мутируем здесь «копию». Это называется «локальной мутацией», когда мы изменяем объект, созданный локально, на эту функцию. Он не оставил этот блок кода, поэтому нет никаких обычных проблем с мутацией.

Объекты

В ES6 мы получили функцию Object.assign, которая принимает любое количество аргументов и объединяет их в первый аргумент. Чтобы сохранить неизменность, мы передаем пустой объект в качестве первого аргумента.

Этот синтаксис немного неуклюж, поэтому есть предложение, проходящее процесс стандартизации, чтобы в конечном итоге быть включенным в JavaScript: распространение объектов. Мы можем использовать его сегодня с Babel, и он очень часто используется в проектах React и Redux. Давайте переработаем приведенный выше пример:

Глубина

В предыдущих примерах мы имели дело с объектами и массивами, содержащими примитивные значения. Что, если у вас есть массив объектов или объект, содержащий другие объекты? Здесь все становится сложнее.

Главный совет, который я могу вам дать, — думать об одном уровне за раз.

Что, если бы у нас был массив пользователей вместо одного пользовательского объекта, и мы хотим обновить пользователя с именем «Джон». Вот тут-то и появляются функциональные утилиты в JavaScript. Мы можем использовать array.map для преобразования каждого объекта в массиве.

Если вы не знакомы с array.map, он берет функцию, вызывает эту функцию для каждого элемента в массиве и строит новый массив с возвращенными значениями.

Теперь, почему мы возвращаем user, а не {...user}? Это позволяет нашему коду предположить, что этот пользователь не изменился при отслеживании изменений. Это может обеспечить огромный прирост производительности, когда задействованы React и Redux. Создавайте новый объект только тогда, когда вам действительно нужно что-то изменить.

Инструмент обновления

Ранее официальное дополнение React, инструмент обновлений предоставляет альтернативный синтаксис для выполнения глубоких обновлений, сохраняя при этом нашу цель создавать новый объект только при необходимости. Возвращаясь к нашему примеру с вложенным объектом, мы можем написать неизменяемый код обновления следующим образом:

Помимо $set есть еще много операций. Если у вас много вложенных объектов (обычно это полезно для ограничения вложенности), этот инструмент может быть полезен. Кому-то нравится этот инструмент, кому-то нет. Со временем я стал просто использовать распространение объектов и методы массива.

Я надеюсь, что это поможет вам понять, как писать код с ограниченными мутациями.