> Что происходит при инициализации массива в React-компоненте при каждом рендере и почему это плохо? (React)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: Альфа-банк
Стек: React
> Пример ответа
Короткий ответ
При каждом рендере React-компонента массив, объявленный внутри тела компонента (например, const arr = []), создаётся заново. Это плохо, потому что:
- если массив передаётся как prop в дочерний компонент, он каждый раз получает новую ссылку, что ломает оптимизации (React.memo, PureComponent)
- если массив используется в
useEffectилиuseMemoкак зависимость, эффект будет выполняться при каждом рендере - увеличивается нагрузка на garbage collector
Подробное объяснение
При каждом вызове функции-компонента создаётся новый execution context, и все переменные, объявленные внутри, инициализируются заново. Для примитивов (числа, строки) это не проблема - они сравниваются по значению. Но для массивов и объектов сравнение идёт по ссылке.
Проблема усугубляется в трёх сценариях:
-
Передача в дочерний компонент: React.memo сравнивает props через shallow comparison. Новый массив всегда !== старому, поэтому мемоизация не срабатывает, и дочерний компонент ререндерится без необходимости.
-
Зависимости хуков: Если массив указан в deps
useEffect/useMemo/useCallback, хук будет выполняться на каждом рендере, так как ссылка меняется. -
Производительность: Частое создание объектов нагружает сборщик мусора, особенно в больших списках или при высокой частоте рендеров (анимации, ввод текста).
На практике
Для статических массивов (констант) выносите их за пределы компонента или используйте 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} />;}// Для динамических данных - useMemofunction 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]} />
> Похожие задачи по frontend
Что такое shallowRef и shallowReactive и каково их назначение во Vue
Может ли useCallback быть нужен для любого React-компонента или есть особенности?
Почему нужно соблюдать порядок вызова хуков в React?
Для чего используется хук useCallback в React
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью