Главная / Статьи / Руководство по собеседованию на React: хуки, отрисовка и вопросы, которые задают крупные компании

Руководство по собеседованию на React: хуки, отрисовка и вопросы, которые задают крупные компании

Виртуальный DOM, подводные камни хуков, производительность, управление состоянием, React 18, упражнения на прямой кодировке и способы объяснения причин повторной отрисовки.

1332 слов

На собеседованиях по React в крупных компаниях редко оценивают умение дословно цитировать документацию. Важнее понимание того, что происходит на уровне алгоритмов — почему компонент перерисовывается, почему эффект срабатывает дважды, почему обновление состояния не отображается сразу. Кандидаты, способные объяснить почему, превосходят тех, кто знает только как.

Ниже приведены типичные темы, которые обычно рассматриваются: основы, хуки, производительность, шаблоны и неожиданные вопросы, встречающиеся повсюду.

1. Основы

Вопросы для разминки почти всегда касаются:

  • Виртуальный DOM — что это такое и почему обновления кажутся быстрее (использование алгоритмов сравнения и согласования вместо прямой записи в DOM).
  • Согласование — как React выбирает, что обновлять (использование ключей, проверка типов элементов).
  • JSX — удобный синтаксис, который компилируется в React.createElement(); это необходимо знать.
  • Контролируемые и неконтролируемые элементы — контролируемые элементы следуют состоянию React (value + onChange); неконтролируемые читают данные из DOM через refs.
  • Props против состояния — props доступны только для чтения сверху; состояние локально и изменяется с помощью функций-установщиков/хуков.
  • Ключи списка — использование индекса в качестве ключа приводит к ошибкам при изменении порядка (неправильное повторное использование DOM/состояния).
  • Интригующий вопрос: «Почему не использовать индекс массива в качестве ключа?» Ответьте на примере перестановки/добавления элемента, при котором 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 — проблемы с одновременными запросами при быстром вводе пользователем.

      9. Тестирование

      • Testing Library — тестирование поведения, а не внутренних механизмов (запросы к ролям/тексту).
      • Jest — имитация API; необходимо знать ограничения на создание снимков состояния.
      • Единичное тестирование против интеграционного и полного тестирования, а также место, где обычно проводятся тесты компонентов.

      10. Живое кодирование с повторениями

      • Задержка поиска/автодополнения
      • Персонализированный useFetch с поддержкой состояний загрузки/ошибок/данных
      • Бесконечное прокручивание или пагинация
      • Модальные окна с использованием порталов (createPortal) и причины их существования (избежание переполнения/некорректного z-index при сохранении связи с деревом React для обработки событий/контекста)
      • Счётчик с функциями отмены/повтора через useReducer
      • Выявление ошибок из-за устаревших закрытий функций или отсутствующих зависимостей

      Пример пошагового руководства

      «Почему у этого 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.