Как исправить некорректный поиск в React-компоненте

    Некорректная работа поиска - частая проблема при разработке на React. Пользователь вводит "1", а система ищет "11" или предыдущие запросы. В этой статье разберём причины и предложим готовое решение с использованием Debounce.

    Почему поиск работает неправильно?

    Основная причина - отсутствие задержки (debounce) при обработке ввода. Каждое нажатие клавиши вызывает новый запрос к серверу, но из-за асинхронности ответы приходят в неправильном порядке. Например, при быстром наборе "111" сначала отправляется запрос для "1", потом для "11", и наконец для "111". Если сервер отвечает медленно, последний ответ может перезаписать правильные данные.

    Проблемы с состоянием (state)

    В исходном коде handleSearchChange сразу обновляет search и вызывает loadItems(true). Это приводит к тому, что каждый символ запускает новый запрос, а предыдущие запросы не отменяются. Результат - перемешивание данных и непредсказуемое поведение.

    Решение: внедрение Debounce

    Debounce - это техника, которая задерживает выполнение функции до тех пор, пока пользователь не прекратит ввод на определённое время (например, 300 мс). Это гарантирует, что запрос отправляется только для финального значения поиска.

    Шаг 1: Установка библиотеки

    Используйте lodash.debounce или напишите свою функцию. Установка через npm:

    npm install lodash.debounce

    Шаг 2: Импорт и создание debounced-функции

    import debounce from 'lodash.debounce';
    
    const debouncedSearch = useCallback(
      debounce((value) => {
        setSearch(value);
        offsetRef.current = 0;
        setItems([]);
        loadItems(true);
      }, 300),
      []
    );

    Шаг 3: Обновление handleSearchChange

    const handleSearchChange = (e) => {
      debouncedSearch(e.target.value);
    };

    Теперь запрос будет отправляться только через 300 мс после последнего ввода.

    Дополнительные улучшения

    Отмена предыдущих запросов

    Используйте AbortController в axios, чтобы отменять незавершённые запросы при новом поиске:

    const controllerRef = useRef(null);
    
    const loadItems = useCallback(async (reset = false) => {
      if (controllerRef.current) controllerRef.current.abort();
      controllerRef.current = new AbortController();
      const res = await axios.get('...', { signal: controllerRef.current.signal });
      // ...
    }, [search]);

    Оптимизация с useRef для search

    Храните актуальное значение поиска в ref, чтобы избежать замыканий:

    const searchRef = useRef(search);
    searchRef.current = search;

    Заключение

    Некорректный поиск в React возникает из-за отсутствия debounce и управления состоянием. Внедрение задержки в 300 мс и отмена предыдущих запросов решает проблему. Используйте предложенные шаги, чтобы поиск работал стабильно и предсказуемо.

    Часто задаваемые вопросы