Содержание
Коротко
Поиск, фильтры и автосохранение не должны отправлять запрос на каждое нажатие клавиши. В React для этого удобно разделить быстро меняющееся значение в поле и действие, которое должно сработать после короткой паузы.
Исходная статья разбирает два хука из @reactuses/core: useDebounce возвращает отложенную копию значения, а useDebounceFn откладывает вызов функции. Такая обёртка избавляет от ручного управления таймерами и типичных ошибок при размонтировании компонента.
Что произошло
В наивной реализации обработчик ввода создаёт или перезапускает setTimeout. Для маленького примера это кажется достаточным, но код быстро обрастает ссылкой на таймер, очисткой и условиями. Одну из этих частей легко пропустить при копировании в другой компонент.
Особенно неприятны два случая: пользователь уже ушёл со страницы, а отложенный запрос всё равно отправляется, или функция видит старое значение свойства либо состояния. Причина во «снимке» окружения, который замыкание сохраняет на момент постановки таймера.
useDebounce(value, wait) решает задачу для данных. Поле ввода по-прежнему получает новое query на каждое нажатие, а эффект подписывается на debouncedQuery, обновляющийся лишь после паузы.
Для действий с аргументами предназначен useDebounceFn(fn, wait). Он возвращает run, cancel и flush: запустить отложенный вызов, отменить ожидающий вызов и выполнить его немедленно.
Почему это важно
Задержка нужна не только ради экономии запросов. Поиск по каталогу, подсказки адреса и проверка формы могут создавать ответ на уже неактуальный ввод. Чем медленнее сервер или сеть, тем заметнее лишняя работа и вероятность показать пользователю устаревший результат.
Разделение состояния интерфейса и побочного действия сохраняет отзывчивость. Если задерживать само значение, привязанное к <input>, текст будет появляться с паузой. Если же отложить только поиск, пользователь видит каждый введённый символ сразу.
Для автосохранения важнее управление жизненным циклом. При нажатии «Опубликовать» стоит вызвать flush, чтобы черновик сохранился до основной операции. При «Отменить» нужен cancel, иначе отброшенный текст может попасть на сервер чуть позже.
Хуки также не обращаются к объектам браузера во время отрисовки. Поэтому их можно использовать в компонентах с серверным рендерингом без отдельной проверки window и без расхождений между серверной и клиентской разметкой.
На практике
Сначала определите, что именно требуется замедлить: значение или команду. Это решение обычно важнее выбора конкретной библиотеки, потому что оно задаёт границы состояния и эффектов в компоненте.
Для поиска достаточно держать две переменные: исходную строку для интерфейса и её отложенную версию для useEffect. Не запускайте запрос, пока отложенная строка пуста, и обрабатывайте отмену самого сетевого запроса отдельно.
- Для поиска, фильтра и ползунка используйте
useDebounce: потребителям нужен итоговый изменённый показатель. - Для автосохранения, журналирования и обработчика изменения размеров используйте
useDebounceFn: действию передаются аргументы. - Вызывайте
cancelпри явном отказе от действия, аflush— перед отправкой формы или переходом к операции, которой нужны актуальные данные. - Оставляйте обычное состояние быстрым: задержка должна касаться побочного действия, а не реакции поля на ввод.
- При длинном непрерывном вводе рассмотрите
maxWait, чтобы обновление всё же происходило с ограниченным интервалом.
Опции leading и trailing помогают настроить момент первого и последнего вызова. Например, leading: true полезен для мгновенной реакции на первое нажатие с подавлением частых повторов, а trailing: true подходит для поиска после окончания ввода.
Не путайте задержку с ограничением частоты. Для прокрутки, перетаскивания и индикатора прогресса обычно нужен useThrottle или useThrottleFn: обновления должны приходить регулярно во время движения, а не только после полной остановки.
Итог
Ручной setTimeout в React почти всегда требует больше кода, чем кажется: надо очищать таймер, не терять свежие данные и не выполнять работу после ухода со страницы. Общий хук собирает эти правила в одном месте.
useDebounce полезен, когда нужна отложенная версия состояния; useDebounceFn — когда отложить нужно саму операцию. Чёткое разделение этих сценариев делает поиск отзывчивее, автосохранение предсказуемее, а компоненты — проще для сопровождения.

