> Как написать кастомный хук useState с колбэком при смене состояния в React (React)

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

Компании: Molecula

Стек: React

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

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

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

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

Стандартный useState в React не предоставляет встроенного механизма для колбэка после обновления состояния, в отличие от setState в классовых компонентах. Это осознанное решение команды React, так как эффекты лучше обрабатывать через useEffect.

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

  • Хранить предыдущее значение состояния через useRef
  • Сравнивать текущее и предыдущее значение
  • Вызывать колбэк только при изменении

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

На практике

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

Ключевые моменты реализации:

  • useRef для хранения предыдущего значения и актуального колбэка
  • useEffect для отслеживания изменений
  • Возврат кортежа [state, setState] для совместимости с обычным useState

Для TypeScript нужно правильно типизировать дженерик и сигнатуру колбэка.

Пример кода

TYPESCRIPT
import { useState, useEffect, useRef, useCallback } from 'react';
function useStateWithCallback<T>(
initialValue: T | (() => T),
callback?: (newValue: T) => void
): [T, React.Dispatch<React.SetStateAction<T>>] {
const [state, setState] = useState<T>(initialValue);
const prevStateRef = useRef<T>(state);
const callbackRef = useRef<((newValue: T) => void) | undefined>(callback);
// Обновляем ссылку на колбэк при каждом рендере
useEffect(() => {
callbackRef.current = callback;
});
// Вызываем колбэк при изменении состояния
useEffect(() => {
if (prevStateRef.current !== state && callbackRef.current) {
callbackRef.current(state);
}
prevStateRef.current = state;
}, [state]);
return [state, setState];
}
// Использование:
const [count, setCount] = useStateWithCallback(0, (newCount) => {
console.log('Count changed to:', newCount);
});

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

Начни с объяснения, почему стандартный useState не имеет колбэка - это архитектурное решение React, основанное на декларативном подходе. Затем опиши два подхода: через useEffect и через ручное отслеживание с useRef.

Подчеркни важность обработки замыканий - используй useRef для колбэка, чтобы избежать проблем с устаревшими ссылками. Упомяни, что в большинстве случаев лучше использовать useEffect напрямую, а кастомный хук оправдан только при частом повторении паттерна.

Будь готов обсудить trade-off: хук добавляет сложность и может привести к неожиданным вызовам колбэка при строгом режиме React (StrictMode).

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

  • Понимание жизненного цикла React и работы хуков
  • Умение работать с useRef для хранения мутабельных значений
  • Знание проблем замыканий в React и способов их решения
  • Способность проектировать абстракции с правильной типизацией
  • Понимание разницы между императивным (колбэк) и декларативным (эффекты) подходами

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

  • Вызов колбэка напрямую в setState (это не работает, setState не принимает второй аргумент в функциональных компонентах)
  • Игнорирование стабильности ссылки на колбэк - передача колбэка без useRef приводит к вызову устаревшей версии
  • Отсутствие проверки на реальное изменение состояния - колбэк вызывается при каждом рендере, даже если состояние не изменилось
  • Неправильная обработка StrictMode - в режиме разработки эффекты вызываются дважды, что может привести к дублирующимся вызовам колбэка
  • Забывание очистки эффекта при размонтировании компонента

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

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