Дайджесты новостей
Концептуальный дуэт предметов-антиподов: скоростной гоночный механизм Mutative 2.0 обгоняет массивную шестереночную передачу Immer 11.2 на треке управления состоянием.

Производительность и состояние в React: Mutative 2.0 против Immer 11.2 и React Window

Иммутабельность состояния (Immutability) — краеугольный камень архитектуры React. Благодаря неизменяемым объектам компоненты могут мгновенно проверять, изменились ли входные данные, через быстрое поверхностное сравнение ссылок (prevProps.user === nextProps.user), избавляясь от дорогостоящих глубоких проверок.

Однако писать чистые иммутабельные обновления вручную с помощью spread-операторов (...state) для глубоко вложенных структур данных — настоящее наказание: легко пропустить уровень вложенности и случайно мутировать исходный стейт.

Сегодня мы разбираем схватку за лидерство в мире иммутабельности: релиз Mutative 2.0, ответное обновление Immer 11.2 и свежую версию виртуализатора списков React Window 2.4.

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

Mutative 2.0

Библиотека Immer долгое время оставалась безальтернативным решением. Но вышедшая библиотека Mutative 2.0 предлагает полную замену Immer по API, но с колоссальным приростом скорости:

  • В бенчмарках на массивах из 50 000 объектов Mutative оказывается в 2–6 раз быстрее Immer.
  • Оптимизированный механизм патчей: генерация RFC 6902 JSON Patch операций происходит без повторного обхода дерева.
  • Поддержка строгого режима иммутабельности и автозаморозки (autoFreeze: true).
import { create } from 'mutative';

interface State {
  todos: { id: number; text: string; done: boolean }[];
  filter: string;
}

const initialState: State = {
  todos: [{ id: 1, text: 'Проверить Mutative 2.0', done: false }],
  filter: 'all',
};

// Функция create работает так же, как produce в Immer, но значительно быстрее!
const nextState = create(initialState, (draft) => {
  draft.todos[0].done = true;
  draft.todos.push({ id: 2, text: 'Оптимизировать рендер списков', done: false });
});

console.log('Исходный стейт не тронут:', initialState.todos[0].done === false);
console.log('Новый стейт иммутабелен:', nextState.todos[0].done === true);

Immer 11.2

Команда проекта Immer ответила точечным, но полезным релизом 11.2: добавлена вспомогательная утилита isModified(draft). Теперь внутри драфта можно проверить, были ли реально изменены поля объекта, и прервать лишние вычисления, если данные остались прежними:

import { produce, isModified } from 'immer';

const next = produce(state, (draft) => {
  // Выполняем условные проверки
  if (!isModified(draft.settings)) {
    console.log('Настройки не менялись, пропускаем валидацию');
  }
});

React Window 2.4

Рендеринг списков на 10 000 элементов без виртуализации гарантированно вешает браузер. Библиотека React Window от Брайана Вогана — признанный эталон решения этой проблемы.

В релизе 2.4 разработчики устранили главную головную боль виртуализации при серверном рендеринге (SSR / Next.js):

  • Улучшенная гидратация: на сервере рендерится фиксированное начальное окно элементов, которое совпадает с клиентом до момента замера скролла, исключая неприятные ошибки Hydration Mismatch.
  • Поддержка динамических размеров элементов: авторасчет высоты строк через ResizeObserver без дергания полосы прокрутки.
import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
  <div style={style} className="p-4 border-b flex items-center">
    Элемент списка #{index}
  </div>
);

export function VirtualizedCatalog({ itemCount }) {
  return (
    <List
      height={600}
      itemCount={itemCount}
      itemSize={60}
      width="100%"
    >
      {Row}
    </List>
  );
}

Итог оптимизации

Понимание того, как работают ссылки в памяти и почему inline-пропсы ломают мемоизацию компонентов, в связке с легкими библиотеками вроде Mutative и React Window, позволяет держать стабильные 60 кадров в секунду даже на самых тяжелых дашбордах.