PassFront
  1. PassFront
  2. База знаний
  3. Хуки

Что нужно useCallback для избежания лишних рендеров?

Хуки

Сам по себе useCallback лишних рендеров не убирает — для этого ему нужны три условия, работающие вместе.

Условие 1: React.memo на компоненте-получателе

Рендер пропускает не useCallback, а React.memo. Стабильная функция без memo-обёртки у ребёнка ничего не даёт — он перерендерится вместе с родителем безусловно:

const List = React.memo(ListInner); // без этого useCallback у родителя бессмыслен

Условие 2: правильные зависимости

const handleSelect = useCallback((id) => {
  onSelect(id, mode);
}, [onSelect, mode]); // все используемые значения — в deps
  • Лишние/нестабильные зависимости → функция пересоздаётся каждый рендер → memo снова ломается.
  • Пропущенные зависимости → ссылка стабильна, но замыкание устарело: обработчик работает со старыми значениями. Это уже баг корректности, не производительности.

Условие 3: стабильность ВСЕХ остальных props

Memo сравнивает каждый проп; один нестабильный — и сравнение провалено:

<List
  onSelect={handleSelect}   // стабильно (useCallback)
  style={{ gap: 8 }}        // ломает всё: новый объект каждый рендер
/>

Инлайн-объекты — в useMemo (или константу вне компонента), children тоже учитывать.

Дополнительно

  • Цепочка работает и для зависимостей эффектов: стабильный колбэк в deps useEffect у ребёнка предотвращает лишние перезапуски эффекта.
  • Проверять результат профайлером: «выглядит мемоизированным» ≠ «не рендерится».

Коротко для собеседования

Чтобы useCallback реально убирал рендеры, нужно: React.memo на получателе (именно он пропускает рендер), корректные полные зависимости (иначе либо нестабильность, либо stale closure) и стабильность всех остальных props (один инлайн-объект ломает сравнение). useCallback — только звено цепочки.

Другие вопросы раздела