Почему мутирование массива считается плохой практикой
Что проверяют
Понимаете ли вы, почему фронтенд-фреймворки и стейт-менеджеры опираются на сравнение по ссылке и что ломается, когда массив меняют на месте. Обычно следом просят найти баг в коде с push и setState или спрашивают, как Redux Toolkit позволяет «мутировать» в редьюсерах.
Как отвечать
Сама мутация не зло, проблема в мутации общих данных, на которые кто-то ещё держит ссылку:
- React не видит изменений:
items.push(x); setItems(items)передаёт тот же массив, React сравнивает черезObject.is, решает, что ничего не поменялось, и может пропустить рендер. То же сReact.memo, зависимостямиuseEffectиuseMemo, селекторами Redux; - побочные эффекты через ссылки: функция вызвала
sort()на пропсах, и порядок поменялся у всех, кто держит этот массив. Такие баги трудно воспроизвести; - история и отладка: неизменяемые снимки состояния дают undo, time travel в Redux DevTools и простое сравнение «до и после».
Мутируют массив методы push, pop, shift, unshift, splice, sort, reverse, fill, copyWithin. Безопасные замены: спред [...arr, x], map, filter, concat, slice и методы ES2023 toSorted(), toReversed(), toSpliced(), with(index, value).
Дальше покажите, что понимаете границы правила. Мутировать локальный массив, который вы только что создали внутри функции, нормально и часто быстрее. Redux Toolkit использует Immer: внутри createSlice вы пишете «мутирующий» код, а библиотека собирает новый объект. Во Vue 3 реактивность построена на Proxy, и прямое изменение массива там штатный способ.
Где спотыкаются
Считают спред глубокой копией. [...users] копирует только верхний уровень, объекты внутри общие, и copy[0].name = ... меняет исходные данные. Для глубокой копии есть structuredClone, но чаще достаточно копировать только изменяемую ветку.
Думают, что const защищает массив. Он запрещает переприсвоить переменную, но не менять содержимое.
Утверждают, что иммутабельность всегда быстрее. Копирование большого массива на каждое изменение стоит O(n), выигрыш даёт только дешёвое сравнение ссылок при рендере.