> Как реализовать кастомный хук для отслеживания первого рендера в React? (React, JavaScript)

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

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

Стек: React, JavaScript

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

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

Кастомный хук useIsFirstRender можно реализовать через useRef: создаём ref с true, в useEffect (или useLayoutEffect) меняем на false. Возвращаем ref.current. Это позволяет отличить первый рендер от последующих без лишних ререндеров. Альтернативно - использовать useMemo с флагом, но ref надёжнее, так как не вызывает повторных вычислений.

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

Основная задача - определить, выполняется ли рендер компонента впервые. React не предоставляет встроенного API для этого, поэтому используем useRef, который сохраняет значение между рендерами без вызова ререндера.

Ключевой момент: useEffect выполняется после того, как React зафиксировал изменения в DOM. Если установить флаг в useEffect, то во время первого рендера ref.current всё ещё будет true, а начиная со второго - false.

useLayoutEffect срабатывает синхронно после всех мутаций DOM, но до того, как браузер отрисует изменения. Разница с useEffect минимальна для этой задачи, но useLayoutEffect может быть предпочтительнее, если нужно избежать визуального мерцания при первом рендере.

Важно: хук не должен вызывать ререндер - поэтому используем useRef, а не useState.

На практике

Хук полезен для:

  • Отключения анимаций при первом рендере (чтобы избежать "вспышки").
  • Условного выполнения эффектов (например, не логировать первый рендер).
  • Реализации паттерна "componentDidMount" в функциональных компонентах.
  • Сравнения предыдущих и текущих пропсов (в комбинации с usePrevious).

Стоит учитывать, что в React 18 Strict Mode useEffect вызывается дважды в development - это может повлиять на логику, если хук используется внутри эффекта. В production такого нет.

Пример кода

JAVASCRIPT
import { useRef, useEffect } from 'react';
function useIsFirstRender() {
const isFirst = useRef(true);
useEffect(() => {
isFirst.current = false;
}, []);
return isFirst.current;
}
// Использование
function MyComponent() {
const isFirstRender = useIsFirstRender();
useEffect(() => {
if (isFirstRender) {
console.log('Первый рендер');
} else {
console.log('Последующий рендер');
}
}, [isFirstRender]);
return <div>Content</div>;
}

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

Начни с краткого объяснения задачи - отличить первый рендер от последующих. Упомяни, что используешь useRef из-за его стабильности и отсутствия ререндеров. Объясни, почему useEffect подходит для установки флага: он выполняется после фиксации в DOM.

Если спросят про альтернативы, скажи, что можно использовать useMemo с пустыми зависимостями, но это менее надёжно, так как useMemo может быть пересчитан React при оптимизациях. Также упомяни useLayoutEffect как альтернативу для синхронного обновления.

Подчеркни, что хук не вызывает лишних ререндеров - это его главное преимущество. Если собеседник спросит про Strict Mode, объясни поведение в development.

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

  • Понимание разницы между useRef, useState, useEffect и useLayoutEffect.
  • Знание жизненного цикла функциональных компонентов.
  • Умение избегать лишних ререндеров.
  • Понимание, когда и зачем нужен такой хук.
  • Способность объяснить trade-off между useEffect и useLayoutEffect.

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

  • Использование useState вместо useRef - это вызывает лишний ререндер при первом вызове.
  • Установка флага внутри рендера (не в эффекте) - тогда флаг всегда будет true, так как код выполняется до завершения рендера.
  • Забывание про Strict Mode - в development эффект может выполниться дважды, что сбросит флаг.
  • Возврат isFirst.current напрямую из хука без учёта времени выполнения - если компонент ререндерится до вызова эффекта, значение может быть некорректным.

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

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