> Что происходит при инициализации массива в React-компоненте при каждом рендере и почему это плохо? (React)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Альфа-банк

Стек: React

> Пример ответа

Короткий ответ

При каждом рендере React-компонента массив, объявленный внутри тела компонента (например, const arr = []), создаётся заново. Это плохо, потому что:

  • если массив передаётся как prop в дочерний компонент, он каждый раз получает новую ссылку, что ломает оптимизации (React.memo, PureComponent)
  • если массив используется в useEffect или useMemo как зависимость, эффект будет выполняться при каждом рендере
  • увеличивается нагрузка на garbage collector

Подробное объяснение

При каждом вызове функции-компонента создаётся новый execution context, и все переменные, объявленные внутри, инициализируются заново. Для примитивов (числа, строки) это не проблема - они сравниваются по значению. Но для массивов и объектов сравнение идёт по ссылке.

Проблема усугубляется в трёх сценариях:

  1. Передача в дочерний компонент: React.memo сравнивает props через shallow comparison. Новый массив всегда !== старому, поэтому мемоизация не срабатывает, и дочерний компонент ререндерится без необходимости.

  2. Зависимости хуков: Если массив указан в deps useEffect/useMemo/useCallback, хук будет выполняться на каждом рендере, так как ссылка меняется.

  3. Производительность: Частое создание объектов нагружает сборщик мусора, особенно в больших списках или при высокой частоте рендеров (анимации, ввод текста).

На практике

Для статических массивов (констант) выносите их за пределы компонента или используйте useMemo с пустыми зависимостями. Для массивов, зависящих от пропсов или состояния, применяйте useMemo с корректными зависимостями.

JSX
// Плохо - массив создаётся при каждом рендере
function BadComponent({ items }) {
const options = ['a', 'b', 'c']; // новая ссылка каждый раз
return <Dropdown options={options} />;
}
// Хорошо - константа вне компонента
const OPTIONS = ['a', 'b', 'c'];
function GoodComponent({ items }) {
return <Dropdown options={OPTIONS} />;
}
// Для динамических данных - useMemo
function DynamicComponent({ filter }) {
const filteredItems = useMemo(
() => items.filter(item => item.includes(filter)),
[filter]
);
return <List items={filteredItems} />;
}

Как отвечать на собеседовании

Начните с ключевой проблемы - создание новой ссылки при каждом рендере. Объясните последствия для мемоизации и зависимостей хуков. Приведите конкретные примеры: React.memo, useEffect, useMemo. Упомяните, что для примитивов проблема неактуальна. Предложите решения: вынос констант, useMemo, useRef для стабильных ссылок. Покажите понимание, когда useMemo оправдан, а когда - premature optimization.

Что проверяет интервьюер

  • Понимание механизма ререндера в React
  • Знание shallow comparison и ссылочной идентичности
  • Умение оптимизировать производительность
  • Понимание trade-off между мемоизацией и её стоимостью
  • Знание lifecycle объектов в JavaScript

Типичные ошибки

  • Утверждение, что массивы в компоненте - это всегда плохо (на самом деле, если массив не передаётся и не используется в зависимостях, проблемы нет)
  • Использование useMemo для всех массивов без необходимости (избыточная мемоизация)
  • Забывание указать зависимости в useMemo, что приводит к багам
  • Путаница между useMemo и useRef для хранения стабильных ссылок
  • Игнорирование проблемы с массивами в JSX, переданными напрямую: <List items={[1,2,3]} />

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью