Как оптимизировать тяжелые запросы на бэкенд при поиске на каждый ввод пользователя?
Что проверяют
Видите ли вы сразу две проблемы: лишние запросы и гонку ответов, когда старый ответ приходит позже нового и затирает выдачу. Следом обычно спрашивают, чем debounce отличается от throttle и как отменить уже отправленный запрос.
Как отвечать
Разложите решение на три уровня: меньше запросов, правильный порядок ответов, дешевле каждый запрос.
- debounce: запрос уходит, когда пользователь перестал печатать на 300 мс или около того. Плюс минимальная длина строки, обычно два или три символа, и отсечение повторов: если после обрезки пробелов строка не изменилась, запрос не нужен;
- отмена: каждый новый запрос отменяет предыдущий через
AbortController, аAbortErrorв обработчике игнорируется. Без этого медленный ответ на «rea» может прийти после ответа на «react» и показать не ту выдачу. В RxJS то же самое даютdebounceTime,distinctUntilChangedиswitchMap; - кэш на клиенте: TanStack Query с ключом
['search', query]не повторяет запрос, если пользователь стёр букву и вернул её, аplaceholderData: keepPreviousDataдержит старые результаты на экране, пока грузятся новые; - экономный ответ: сервер отдаёт первые 10 подсказок с нужными полями, а не весь список. Популярные GET-запросы можно кэшировать через
Cache-Control; - маленький набор данных: справочник на пару тысяч записей проще загрузить один раз и фильтровать локально, без сети.
С бэкендом стоит договориться о поиске по индексу: префиксный поиск, pg_trgm или отдельный поисковый движок вместо LIKE '%...%'. Помните, что useDeferredValue в React облегчает рендер, но число запросов не уменьшает. Параметр кодируйте через encodeURIComponent или URLSearchParams.
Где спотыкаются
Ставят debounce и считают задачу решённой. Задержка уменьшает число запросов, но от гонки ответов не спасает, её закрывает только отмена или проверка актуальности.
Создают debounce-функцию в теле компонента React без useMemo или useRef. На каждом рендере получается новый таймер, и задержка фактически не работает.
Путают debounce с throttle. Throttle шлёт запрос раз в интервал прямо во время ввода: это уместно для прокрутки, а для поиска обычно хуже.