На собеседовании по React чаще всего спрашивают о том, как происходит рендеринг и reconciliation, о правилах хуков и массиве зависимостей useEffect, о мемоизации, управлении состоянием, производительности и - для позиций уровня middle - о Next.js и серверном рендеринге. Интервьюер проверяет не определения, а понимание механизма: почему React работает именно так и как вы применяли это в проектах.
Вопросы по React повторяются из компании в компанию. Хорошая новость: их можно подготовить. Плохая: заученное определение распознаётся сразу, стоит задать один уточняющий вопрос. Ниже - ключевые темы, типичные формулировки и то, что за ними хотят услышать.
Рендеринг и reconciliation
Что происходит, когда меняется состояние компонента
Хороший ответ: React планирует повторный рендер, вызывает функцию компонента заново, получает новое дерево элементов, сравнивает его с предыдущим и применяет к DOM только разницу. Сравнение и есть reconciliation. Важно упомянуть, что при рендере родителя по умолчанию перерисовываются и все дочерние компоненты - даже если их пропсы не изменились.
Рендер и коммит
Сильный кандидат разделяет две фазы. В фазе рендера React вызывает компоненты и вычисляет, что должно измениться, - эта фаза может прерываться и повторяться. В фазе коммита изменения применяются к DOM, после чего запускаются эффекты. Отсюда вывод: функция компонента должна быть чистой, без побочных эффектов.
Ключи в списках
Ключ помогает сопоставить элементы между рендерами. Если использовать индекс массива, при вставке элемента в начало списка React решит, что изменились все элементы, и состояние полей ввода «переедет» к соседям. Ключ должен быть стабильным идентификатором из данных.
Батчинг обновлений
Несколько вызовов setState в одном обработчике объединяются в один рендер. Начиная с React 18 это работает и в промисах, и в таймерах. Отсюда частый вопрос с подвохом: почему после setState значение состояния в той же функции ещё старое. Ответ: состояние обновится при следующем рендере, а для вычисления от предыдущего значения нужна функциональная форма обновления.
Хуки и их правила
Почему хуки нельзя вызывать в условиях и циклах
React хранит состояние хуков по порядку их вызова. Если порядок меняется между рендерами, состояние «съезжает» к другому хуку. Поэтому хуки вызываются только на верхнем уровне компонента или другого хука. Линтер eslint-plugin-react-hooks проверяет это автоматически.
useEffect и массив зависимостей
Нужно понимать, когда эффект запускается, зачем функция очистки и почему в зависимости попадает всё, что используется внутри. Функция очистки вызывается перед следующим запуском эффекта и при размонтировании - в ней отписываются от событий, отменяют таймеры и запросы. Бонус - объяснить, что многие эффекты не нужны вовсе: производные данные лучше вычислять при рендере, а реакцию на действие пользователя - писать в обработчике события.
Почему эффект срабатывает дважды
В режиме разработки со StrictMode React намеренно монтирует, размонтирует и снова монтирует компонент, чтобы выявить эффекты без корректной очистки. В продакшене такого нет. Правильный ответ - не отключать StrictMode, а написать очистку так, чтобы повторный запуск был безопасен.
useMemo и useCallback
Оба мемоизируют между рендерами: useMemo - значение, useCallback - функцию. Хороший ответ включает, когда это имеет смысл (дорогие вычисления, стабильные ссылки для React.memo и зависимостей эффектов) и когда только добавляет сложности. Мемоизация сама по себе не бесплатна: React хранит значение и сравнивает зависимости на каждом рендере.
useRef
Ref хранит изменяемое значение, которое переживает рендеры и при изменении не вызывает новый рендер. Используется для доступа к DOM-элементам, хранения таймеров, предыдущих значений и любых данных, которые не должны влиять на отображение.
Собственные хуки
Часто просят написать хук: useDebounce, useFetch, useLocalStorage, useOnClickOutside. Проверяют, умеете ли вы вынести логику из компонента, правильно указать зависимости и сделать очистку. Хороший хук имеет понятный интерфейс и не знает ничего о разметке.
Управление состоянием
- Чем локальное состояние отличается от глобального и когда поднимать состояние выше.
- Когда хватит Context, а когда нужен Redux Toolkit или Zustand.
- Почему данные с сервера лучше держать в TanStack Query или RTK Query, а не в глобальном сторе.
- Зачем хранить фильтры и пагинацию в URL.
Context и лишние рендеры
Когда меняется значение контекста, перерисовываются все его потребители. Если положить в один контекст часто меняющиеся данные и редко меняющиеся настройки, будут перерисовываться лишние компоненты. Решения: разделить контексты, мемоизировать значение провайдера, для частых обновлений использовать стейт-менеджер с подписками на часть состояния.
Серверное и клиентское состояние
Данные с сервера - это кеш чужого состояния: они устаревают, их нужно перезапрашивать, дедуплицировать запросы и обрабатывать ошибки. Библиотеки вроде TanStack Query делают это из коробки. Хранить такие данные в Redux вручную - источник ошибок и лишнего кода. Этот вопрос хорошо показывает, работали ли вы с реальным приложением.
Формы
Спрашивают разницу между контролируемыми и неконтролируемыми компонентами, как организовать валидацию и почему большие формы на контролируемых полях могут тормозить. Хороший ответ упоминает React Hook Form и схемы валидации вроде Zod: форма рендерится реже, а правила валидации описаны в одном месте и переиспользуются.
Производительность
Спрашивают, как найти и устранить лишние рендеры: React Profiler, React.memo, разделение контекстов, виртуализация длинных списков, ленивая загрузка компонентов через lazy и Suspense. Покажите, что сначала измеряете, а потом оптимизируете: преждевременная мемоизация всего подряд - тоже ошибка, и интервьюеры это знают.
- Как найти компонент, который рендерится слишком часто.
- Почему объект или функция, созданные в рендере, ломают React.memo у дочернего компонента.
- Как отрисовать список из десятков тысяч строк без зависаний.
- Как уменьшить размер бандла: разделение кода, динамический импорт, анализ зависимостей.
Next.js и рендеринг на сервере
Для позиций уровня middle почти всегда спрашивают разницу между SSR, SSG, ISR и CSR и когда что выбирать. SSR генерирует страницу на каждый запрос, SSG - на этапе сборки, ISR пересобирает статические страницы по расписанию или по событию, CSR отдаёт пустую оболочку и рендерит всё в браузере.
В App Router добавляются серверные и клиентские компоненты. Серверные компоненты выполняются только на сервере, могут обращаться к данным напрямую и не попадают в клиентский бандл. Клиентские помечаются директивой use client и нужны там, где есть состояние, эффекты и обработчики событий. Частый вопрос - где провести эту границу и как передать данные с сервера на клиент.
Полезно понимать файловый роутинг, layout и загрузочные состояния, а также встроенные оптимизации изображений и шрифтов.
Тестирование
Спрашивают, что и как вы тестируете: логику в хуках и утилитах - unit-тестами на Jest или Vitest, компоненты - через React Testing Library с проверкой поведения, а не внутренней реализации. Хороший ответ объясняет, почему тест должен искать элементы так, как их видит пользователь, - по роли и тексту, а не по классам.
Обработка ошибок и продвинутые темы
Error boundaries
Ошибка при рендере одного компонента по умолчанию ломает всё приложение. Error boundary перехватывает ошибки в своём поддереве и показывает запасной интерфейс. Важно знать ограничения: границы не ловят ошибки в обработчиках событий и асинхронном коде - их нужно обрабатывать отдельно.
Порталы и передача ref
Портал рендерит элемент в другом месте DOM - например, модальное окно в конце body, - сохраняя его положение в дереве React: события всплывают по дереву компонентов, а контекст доступен. Передача ref в дочерний компонент нужна, когда родителю требуется доступ к DOM-элементу внутри, например чтобы поставить фокус.
Конкурентные возможности
На позицию middle могут спросить про useTransition и useDeferredValue: они позволяют пометить обновление как несрочное, чтобы ввод оставался отзывчивым, пока тяжёлый список перерисовывается. Достаточно понимать идею приоритетов обновлений и уметь привести пример.
React и TypeScript
Всё чаще вопросы по React задают в связке с TypeScript: как типизировать пропсы и children, как описать компонент, принимающий пропсы нативного элемента, как типизировать собственный хук с дженериком, чем тип события изменения поля отличается от события клика. Эти вопросы проверяют, писали ли вы типизированный код в реальном проекте.
Вопросы, которые стоит задать интервьюеру
В конце технического интервью обычно дают возможность задать вопросы. Это тоже часть оценки: вопросы показывают, что вам важно. Спросите, на какой версии React и с каким роутингом работает проект, как организовано управление состоянием, есть ли код-ревью и тесты, как устроен процесс релиза, какие задачи ждут нового разработчика в первые месяцы. Ответы помогут и вам понять, подходит ли команда.
Вопросы для уровня middle: архитектура
На позицию middle всё чаще спрашивают не про отдельные хуки, а про организацию приложения. Как разделить код на слои и фичи, чтобы команда не мешала друг другу. Где проходит граница между компонентами интерфейса и бизнес-логикой. Как организовать работу с API: генерация клиента по схеме, единая обработка ошибок, типизация ответов. Как устроить дизайн-систему и переиспользуемые компоненты. Отвечать на такие вопросы проще всего на примере собственного проекта: какая структура была, какие проблемы она создала и как вы их решили.
Методологии вроде Feature-Sliced Design упоминают часто. Знать их полезно, но важнее объяснить, какую проблему решает выбранный подход: предсказуемые зависимости между модулями, понятное место для нового кода, изоляция фич.
Ещё одна частая тема - доступность интерфейса: семантическая разметка, работа с клавиатуры, подписи для полей и кнопок. Вопросы о ней показывают, думаете ли вы о всех пользователях продукта, а не только о тех, кто пользуется мышью.
Как готовиться к вопросам по React
- Составьте список тем из этой статьи и отметьте, по каким можете ответить вслух без подготовки.
- Для каждой слабой темы прочитайте раздел официальной документации React - она подробно объясняет механизмы и типичные ошибки.
- Напишите небольшой пример для каждой темы: эффект с очисткой, мемоизированный список, собственный хук.
- Подготовьте для каждой темы пример из своего проекта.
- Проговорите ответы вслух, а лучше пройдите мок-собеседование.
Вопросы с подвохом
- Можно ли менять состояние напрямую? Нет: React сравнивает ссылки, и мутация объекта не вызовет рендер. Нужно создавать новый объект или массив.
- Что будет, если не указать массив зависимостей у useEffect? Эффект будет запускаться после каждого рендера.
- Почему компонент рендерится, хотя пропсы не изменились? Перерисовался родитель, изменился используемый контекст или в пропсы передаётся новый объект на каждом рендере.
- Зачем нужен Fragment? Чтобы вернуть несколько элементов без лишней обёртки в DOM.
- Чем отличаются пропсы и состояние? Пропсы приходят от родителя и не меняются компонентом, состояние принадлежит компоненту и меняется через сеттер.
Как отвечать, чтобы вас запомнили
- Дайте короткое определение.
- Объясните механизм - как это работает внутри.
- Приведите пример из своего проекта.
- Назовите подводные камни и как вы их обходили.
Ответ «useMemo нужен для оптимизации» звучит одинаково у всех. Ответ с примером, где мемоизация не помогла и почему, запоминается.
Если вопрос незнакомый, не молчите: скажите, что знаете о смежной теме, и порассуждайте вслух. Интервьюеру важно, как вы думаете, - это ещё сильнее проявляется на лайвкодинге.
Такую структуру отрабатывают на мок-собеседованиях. Если хотите потренироваться с ментором, посмотрите трек Frontend на React и как проходит менторство.