Руководство по собеседованию на React: хуки, отрисовка и вопросы, которые задают крупные компании
Виртуальный DOM, подводные камни хуков, производительность, управление состоянием, React 18, упражнения на прямой кодировке и способы объяснения причин повторной отрисовки.
На собеседованиях по React в крупных компаниях редко оценивают умение дословно цитировать документацию. Важнее понимание того, что происходит на уровне алгоритмов — почему компонент перерисовывается, почему эффект срабатывает дважды, почему обновление состояния не отображается сразу. Кандидаты, способные объяснить почему, превосходят тех, кто знает только как.
Ниже приведены типичные темы, которые обычно рассматриваются: основы, хуки, производительность, шаблоны и неожиданные вопросы, встречающиеся повсюду.
1. Основы
Вопросы для разминки почти всегда касаются:
- Виртуальный DOM — что это такое и почему обновления кажутся быстрее (использование алгоритмов сравнения и согласования вместо прямой записи в DOM).
- Согласование — как React выбирает, что обновлять (использование ключей, проверка типов элементов).
- JSX — удобный синтаксис, который компилируется в
React.createElement(); это необходимо знать.
value + onChange); неконтролируемые читают данные из DOM через refs.Интригующий вопрос: «Почему не использовать индекс массива в качестве ключа?» Ответьте на примере перестановки/добавления элемента, при котором React привязывает состояние к неверной строке.
2. Хуки (самая объемная часть собеседования)
useState
- Обновления происходят асинхронно и пакетно.
- Устаревшие замыкания: несколько вызовов
setCount(count + 1)в одном событии используют один и тот жеcount;setCount(prev => prev + 1)решает эту проблему.
useEffect
- Зависимости:
[]один раз, опускаются при каждой отрисовке, указываются только тогда, когда они меняются. - Время и цель очистки (утечки памяти, отмена подписок/запросов).
- Почему эффекты выполняются дважды в режиме разработки — строгий режим React 18 вызывает их дважды, чтобы выявить отсутствующую очистку. Почти каждый сталкивается с этим хотя бы раз.
useMemo против useCallback
useMemoхранит в кэше значение;useCallbackхранит в кэше идентичность функции.- Оба способа обеспечивают сохранение ссылочного равенства для детей
React.memoили массивов зависимостей. - Чрезмерное использование имеет свои издержки; собеседники любят слышать, что мемоизация не является бесплатной.
useRef
- Изменяемое значение между отрисовками без повторной отрисовки.
- Узлы DOM, предыдущие значения, идентификаторы таймеров.
useContext
- Прекращает обработку пропсов для поддерева.
- Важно: обновление контекста перерисовывает все компоненты-потребители в поддереве, включая те, которые читают лишь одно поле.
useReducer
- Лучше использовать, когда логика сложная или несколько полей обновляются одновременно (локальные редьюсеры в стиле Redux).
Пользовательские хуки
- Будьте готовы писать
useDebounce,useFetchилиuseLocalStorage«на лету» — это одно из самых распространенных практических заданий.
3. Рендеринг и производительность
На средних/высоких уровнях здесь происходит отбор сильнейших.
- Почему происходит перерисовка? При перерисовке родительского компонента, изменении состояния, изменении контекста или при использовании пропсов с одинаковым содержимым, но новыми ссылками.
- React.memo — поверхностное сравнение пропсов; бесполезен, если каждый раз передаются новые объекты/функции (используйте вместе с
useMemo/useCallback).
React.lazy + Suspense.react-window для обработки очень больших списков.Классическая проблема: список из 10 000 строк тормозит при вводе запроса. Решения — использование функций debounce, виртуализация и мемоизированные элементы списка.
4. Управление состоянием
- Локальное, глобальное и серверное состояние — отдельное обозначение серверного состояния (кэш, повторная верификация, загрузка/ошибка) от состояния интерфейса производит хорошее впечатление на экспертов.
- Context против Redux/Zustand — Context подходит для редких глобальных параметров (тема, аутентификация); Redux/Zustand — когда нужны промежуточные компоненты, инструменты, сложные обновления или частые записи без проблем с контекстом.
- React Query / SWR / TanStack Query — кэширование, фоновое обновление данных, удаление дубликатов, что позволяет не создавать заново функцию
useEffectдля загрузки данных. - Основы Redux (если стек использует его): действия, редюсеры, хранилище данных, промежуточные компоненты типа thunk/saga, чистые редюсеры.
5. Шаблоны проектирования
- HOCs — обертка вокруг компонента; классический пример —
withAuth. - Render props — общий доступ к логике через функцию-проп; в основном заменены хуками, но концепция всё ещё важна.
- Компоненты-составы — компоненты-братья, обменивающиеся состоянием через контекст (
Select/Select.Option). - Контейнерные/презентационные компоненты — разделение данных и пользовательского интерфейса; с хуками это менее строго регламентировано, но разделение ответственностей по-прежнему важно.
- Композиция вместо наследования — предпочтительный подход React к повторному использованию кода; будьте готовы его обосновать.
6. Жизненный цикл класса (всё ещё часто задают)
Команды, которые предпочитают хуки, всё ещё исследуют основы или устаревшие кодовые базы.
componentDidMount≈useEffect(() => {}, [])componentDidUpdate≈ эффект с зависимостямиcomponentWillUnmount≈ очистка эффектов- Границы ошибок — доступны только для классов (
componentDidCatch/getDerivedStateFromError); у хуков нет аналога, поэтому существуют такие обёртки, какreact-error-boundary.
7. Знание нововведений React 18+
- Одновременная отрисовка — возможность прерывания операций для более быстрой реакции интерфейса.
useTransition— помечает обновления, не являющиеся срочными, чтобы ввод оставался быстрым.useDeferredValue— отложение отображения некритичных элементов интерфейса.- Автоматическое группирование — объединение операций внутри обещаний, таймаутов и нативных обработчиков (не только обработчиков React).
- Пауза загрузки данных — особенно важно при использовании стеков в стиле Next.js/Remix.
- Серверные компоненты — где выполняется код и почему уменьшаются размеры клиентских бандлов.
8. JavaScript, который проникает незамеченным
- Закрытия функций — устаревшие состояния хуков.
- Цикл событий / микро- и макрозадачи — почему происходит такое группирование операций.
- Привязка переменной
this— в случае использования классов. - Debounce против throttle — практически всегда рекомендуется для «оптимизации поискового поля».
- Поверхностное против глубокого сравнения —
React.memo/useMemoи почему литералы объектов мешают мемоизации. - Promises / async-await — проблемы с одновременными запросами при быстром вводе пользователем.
- Testing Library — тестирование поведения, а не внутренних механизмов (запросы к ролям/тексту).
- Jest — имитация API; необходимо знать ограничения на создание снимков состояния.
- Единичное тестирование против интеграционного и полного тестирования, а также место, где обычно проводятся тесты компонентов.
- Задержка поиска/автодополнения
- Персонализированный
useFetchс поддержкой состояний загрузки/ошибок/данных - Бесконечное прокручивание или пагинация
- Модальные окна с использованием порталов (
createPortal) и причины их существования (избежание переполнения/некорректного z-index при сохранении связи с деревом React для обработки событий/контекста) - Счётчик с функциями отмены/повтора через
useReducer - Выявление ошибок из-за устаревших закрытий функций или отсутствующих зависимостей
9. Тестирование
10. Живое кодирование с повторениями
Пример пошагового руководства
«Почему у этого useEffect возник бесконечный цикл?»
useEffect(() => {
setData({ ...data, updated: true });
}, [data]);
Основной ответ: в функции эффекта data указан как зависимость, после чего в data записывается новый объект, поэтому при каждом запуске меняется зависимость и сразу же происходит повторная обработка. Для исправления следует исключить data из списка зависимостей, когда повторная обработка не требуется, вынести обновление вне функции эффекта или использовать функциональный обновитель с более узким набором зависимостей.
Объяснение причин сбоя — а не просто предложение патча — и отличает качественные ответы на собеседованиях по React.
Заключение
Глубина понимания важнее запоминания поверхностных аспектов API. Специалисты, проводящие собеседования, хотят узнать о поведении рендеринга, закрытых функциях и компромиссах в производительности — о тех ошибках, которые возникают в реальных условиях. Подготовьтесь, создавая небольшие компоненты, которые намеренно выдают ошибки (устаревшие закрытые функции, отсутствующие зависимости, избыточные перерендеринги), и исправляя их. Именно этот инстинкт отладки является ключевым критерием оценки на собеседовании.
При практике ставьте себе временные ограничения, как это делается на собеседованиях: объясните концепцию Virtual DOM за шестьдесят секунд, затем отладьте пример с устаревшей закрытой функцией, после чего набросайте схему работы дебаунсированного поиска. Такая последовательность соответствует реальному ходу собеседования.
Как обычно развиваются ситуации на собеседованиях
Упражнения на разминку касаются Виртуального DOM, управляемых вводов и клавиш. На среднем уровне рассматриваются хуки: пакетные обновления, проблема двойных эффектов в режиме Strict Mode, сравнение memo и callback, а также написание собственного хука в реальном времени. На продвинутом уровне обсуждаются вопросы производительности — списки из десяти тысяч строк, проблема context thrash, данные профилятора — и архитектурные решения между Context, Redux/Zustand и TanStack Query для управления состоянием на сервере.
Сохраняйте личный репозиторий с «намеренно сломанными» примерами: счётчик stale-closure, эффект отсутствующих зависимостей, мемоизированный список, который всё равно перерисовывается из-за встроенных объектов, и модальное окно типа portal. Описание этих четырёх способов исправления вслух покрывает удивительно большую часть времени, отведённого на живое программирование и работу с доской.
Если в описании должности упоминается React 18+, будьте готовы дать по одному предложению о параллельном отрисовывании, переходах, отложенных значениях, автоматическом пакетировании и серверных компонентах. Глубокое понимание одного реального примера использования лучше, чем поверхностное знание всех RFC.