useDeferredValue React 19: как Scheduler меняет правила игры с частыми обновлениями
useDeferredValue React 19: как Scheduler меняет правила игры с частыми обновлениями Раньше задача управления частыми обновлениями в React решалась через lodash.debounce или кастомные хуки с риском протухших замыканий. В production это приводило к потере ввода, лагам интерфейса и спаму сетевых запросов. React 19 меняет подход: useDeferredValue в связке с Scheduler сохраняет все обновления, но расставляет приоритеты. Как это работает В отличие от debounce, который отбрасывает лишние вызовы, useDeferredValue не теряет данные. Он разделяет обновления на срочные (ввод текста) и фоновые (тяжелые вычисления). React сам решает, когда выполнить вторые, используя механизм приоритетов Scheduler. По умолчанию задержка до 300 мс, регулируемая через Scheduler.unstable_shouldYield. Пример для поиска с тяжелой фильтрацией: const [query, setQuery] = useState(''); const deferredQuery = useDeferredValue(query); const results = useMemo(() => { return heavySearch(deferredQuery); }, [deferredQuery]); Здесь query обновляется мгновенно — инпут не тормозит. A deferredQuery React подтягивает позже, сохраняя отзывчивость. Где это критически важно useDeferredValue незаменим, когда все изменения важны и их нельзя терять. Типичный production-сценарий: поиск с автодополнением, где каждое нажатие клавиши должно быть учтено, но фильтрация результатов — тяжелая операция. Или рендер списка с вложенными компонентами при скролле. Типичная ошибка Начинающие разработчики применяют useDeferredValue для контроля сетевых запросов. Это не работает: если пользователь кликает каждые 100 мс, на сервер полетит столько же запросов. useDeferredValue не сбрасывает их и не отменяет — он только откладывает рендер. Для API-вызовов все еще нужен debounce. Когда debounce остается Старый добрый debounce незаменим в трех случаях: строгий тайминг после последнего действия (отправка формы, автосохранение), сброс предыдущих запросов, и контроль количества вызовов API. useDeferredValue для этого не подходит. Вывод: useDeferredValue — это не замена debounce, а новый инструмент для UI-оптимизаций внутри компонента, который требует осознанного выбора между потерей данных и отзывчивостью.