ReactSeniorTechnical

Что такое React Compiler (стабилен в React 19) и как он меняет ручную мемоизацию?

React Compiler — статический AOT-плагин (Babel/SWC), который анализирует компоненты и хуки, выводит зависимости и автоматически вставляет мемоизацию. В типичном проекте useMemo/useCallback/React.memo становятся почти не нужны: компилятор делает это точнее и не хуже разработчика вручную.

Разбор

React Compiler — это AOT-плагин (Babel/SWC), поставляемый как babel-plugin-react-compiler и стабилизированный в React 19. Он читает функциональные компоненты и кастомные хуки, строит control-flow граф, выводит зависимости каждого выражения и переписывает код так, чтобы значения с неизменившимися зависимостями повторно использовались между рендерами. Это устраняет главную причину, по которой команды раньше засыпали код useMemo/useCallback.

Что компилятор делает внутри

Для каждого компонента компилятор:

  1. Строит SSA-форму (Static Single Assignment) для отслеживания зависимостей.
  2. Определяет «реактивные» значения — те, что зависят от props, state или контекста.
  3. Оборачивает стабильные вычисления в эквивалент useMemo с автоматически выведенными зависимостями.
  4. Стабилизирует колбэки — эквивалент useCallback без ручного списка зависимостей.
  5. Применяет аналог React.memo к дочерним JSX-элементам, чтобы стабильные ссылки реально пропускали рендер потомков.

Если компилятор не уверен в чистоте функции — он просто не оптимизирует её. Отказ безопасен: код работает как раньше, только без кэширования.

Пример

// До: разработчик вручную контролирует зависимости
const items    = useMemo(() => list.filter(byQuery(q)), [list, q]);
const onSelect = useCallback((id: string) => setSelected(id), []);

// После React Compiler: идиоматический JS без ручной мемоизации
// Компилятор сам определит, что items зависит от list и q,
// а onSelect — стабильная функция
const items = list.filter(byQuery(q));
function onSelect(id: string) {
  setSelected(id);
}

Интеграция

# Next.js (встроено в 15+)
# next.config.ts
experimental: { reactCompiler: true }

# Vite
npm install babel-plugin-react-compiler
# vite.config.ts → plugins: [react({ babel: { plugins: ['babel-plugin-react-compiler'] } })]

# ESLint-плагин для диагностики нарушений React Rules
npm install eslint-plugin-react-compiler

ESLint-плагин eslint-plugin-react-compiler подсвечивает участки, мешающие оптимизации: мутации входных массивов, нарушения правил хуков, условные вызовы хуков.

Подводные камни

  • Скрытые мутации ломают компилятор. Если компонент мутирует props или внешний объект во время рендера, компилятор либо откажется от оптимизации, либо закэширует неправильный результат. Перед включением прогоните eslint-plugin-react-compiler.
  • Компилятор не ускоряет DOM, сеть и layout-эффекты. Он оптимизирует только render-фазу. Медленные useEffect и тяжёлые layout-вычисления остаются вашей ответственностью.
  • Не удаляйте React.memo и useMemo скопом. На участках, где компилятор отказался оптимизировать (нарушение React Rules), ручная мемоизация остаётся единственной защитой. Удаление без проверки профайлером создаст регрессии.
  • Классовые компоненты не поддерживаются. Компилятор работает только с функциональными компонентами. Наличие в кодовой базе классовых компонентов не сломает сборку, но они не получат оптимизации.
  • Постепенное включение безопаснее. Включайте по директиве 'use memo' на файл или директорию, наблюдайте Performance Profiler в production, потом расширяйте охват. Резкое включение на весь монорепо с legacy-кодом рискованно.
  • Не путайте с RSC. React Compiler и React Server Components — независимые механизмы с разными задачами. Компилятор оптимизирует render-фазу клиентских компонентов; RSC устраняет их клиентский JS полностью. Они хорошо работают вместе.
  • Отладка становится сложнее. Сгенерированный код читается труднее оригинального. В DevTools Profiler можно видеть «memoized» компоненты, но понять, почему конкретная функция не закэширована, без знания правил компилятора сложно.
  • Версия React строго 19+. Если в монорепо есть пакеты с peer dependency react: "^18", компилятор может конфликтовать с ними до обновления зависимостей.

Common mistakes

  • Считать, что компилятор всё исправит, и не править нарушения правил React.
  • Удалять useMemo/useCallback без прогонки тестов и профилировщика.
  • Игнорировать eslint-plugin-react-compiler-предупреждения.

What the interviewer is testing

  • Может ли описать, какие правила должен соблюдать код.
  • Знает ли, что компилятор может отказаться оптимизировать модуль.
  • Понимает ли, что RSC и компилятор — разные вещи.

Sources

Related topics