> Как написать кастомный хук useState с колбэком при смене состояния в React (React)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: Molecula
Стек: React
> Пример ответа
Короткий ответ
Кастомный хук с колбэком при смене состояния можно реализовать через useEffect, который отслеживает изменения состояния и вызывает переданный колбэк. Альтернативный подход - использовать useRef для хранения предыдущего значения и вызывать колбэк вручную при каждом обновлении. Оба варианта требуют аккуратной обработки зависимостей и стабильности ссылок на колбэк.
Подробное объяснение
Стандартный useState в React не предоставляет встроенного механизма для колбэка после обновления состояния, в отличие от setState в классовых компонентах. Это осознанное решение команды React, так как эффекты лучше обрабатывать через useEffect.
Основная сложность - обеспечить вызов колбэка только при реальном изменении состояния, а не при каждом рендере. Для этого нужно:
- Хранить предыдущее значение состояния через useRef
- Сравнивать текущее и предыдущее значение
- Вызывать колбэк только при изменении
Важно учитывать стабильность ссылки на колбэк - если передавать колбэк напрямую, он может быть устаревшим из-за замыканий. Решение - использовать useRef для хранения актуального колбэка.
На практике
Хук принимает начальное значение и опциональный колбэк, который будет вызван при каждом изменении состояния. Колбэк получает новое значение состояния в качестве аргумента.
Ключевые моменты реализации:
- useRef для хранения предыдущего значения и актуального колбэка
- useEffect для отслеживания изменений
- Возврат кортежа [state, setState] для совместимости с обычным useState
Для TypeScript нужно правильно типизировать дженерик и сигнатуру колбэка.
Пример кода
TYPESCRIPTimport { 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 - в режиме разработки эффекты вызываются дважды, что может привести к дублирующимся вызовам колбэка
- Забывание очистки эффекта при размонтировании компонента
> Похожие задачи по frontend
Корректно ли генерировать уникальный id для ключей в React при отсутствии id в данных с бэка и вызовет ли это проблемы с оптимизацией рендера
Есть ли проблемы при инициализации useState функцией в React и как их решить
Можно ли условно вызывать хуки в React при разных рендерах
Какой хук в React соответствует поведению componentDidMount и componentWillUnmount
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью