← Назад к вопросам

Как оптимизировать отправку запроса из поисковой строки на стороне пользователя?

2.2 Middle🔥 111 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Оптимизация поисковых запросов

Поисковая строка - это классический case, где нужно оптимизировать количество запросов к серверу. Если просто отправлять запрос при каждом изменении input, получишь катастрофу с производительностью и нагрузкой на сервер.

Debouncing - основной инструмент

// hooks/useDebounce.ts
import { useState, useEffect } from 'react';

export function useDebounce<T>(value: T, delay: number = 300): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value);
  
  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
    
    return () => clearTimeout(handler);
  }, [value, delay]);
  
  return debouncedValue;
}

Этот хук откладывает обновление значения на заданное время (обычно 300-500ms). Пока пользователь печатает, таймер перезапускается, и запрос отправляется только когда пользователь остановился.

Применение в компоненте поиска

// components/SearchQuestions.tsx
import { useState, useCallback } from 'react';
import { useDebounce } from '@/hooks/useDebounce';
import { fetchAPI } from '@/lib/api';

interface SearchResult {
  id: string;
  title: string;
  profession_id: string;
}

export function SearchQuestions() {
  const [input, setInput] = useState('');
  const [results, setResults] = useState<SearchResult[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  
  const debouncedInput = useDebounce(input, 300);
  
  useEffect(() => {
    if (!debouncedInput.trim()) {
      setResults([]);
      return;
    }
    
    const fetchResults = async () => {
      try {
        setLoading(true);
        const data = await fetchAPI<SearchResult[]>('/api/v1/questions/search', {
          params: { q: debouncedInput },
        });
        setResults(data);
      } catch (err) {
        setError(err instanceof Error ? err : new Error('Search failed'));
      } finally {
        setLoading(false);
      }
    };
    
    fetchResults();
  }, [debouncedInput]);
  
  return (
    <div className="flex flex-col gap-4">
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder="Поиск вопросов..."
        className="border border-border-default rounded-lg px-4 py-2"
      />
      
      {loading && <p className="text-content-secondary">Поиск...</p>}
      {error && <p className="text-red-500">Ошибка: {error.message}</p>}
      
      <ul className="space-y-2">
        {results.map((result) => (
          <li key={result.id} className="p-3 bg-surface-secondary rounded">
            {result.title}
          </li>
        ))}
      </ul>
    </div>
  );
}

Дополнительные оптимизации

// Кэширование результатов
const searchCache = new Map<string, SearchResult[]>();

async function searchWithCache(query: string) {
  if (searchCache.has(query)) {
    return searchCache.get(query);
  }
  
  const results = await fetchAPI<SearchResult[]>('/api/v1/questions/search', {
    params: { q: query },
  });
  
  searchCache.set(query, results);
  return results;
}

// Отмена предыдущих запросов (AbortController)
let abortController: AbortController | null = null;

const fetchResults = async () => {
  if (abortController) {
    abortController.abort();
  }
  
  abortController = new AbortController();
  
  try {
    const data = await fetch('/api/v1/questions/search', {
      signal: abortController.signal,
      params: { q: debouncedInput },
    });
  } catch (err) {
    if (err instanceof DOMException && err.name === 'AbortError') {
      return;
    }
    setError(err);
  }
};

Минимальная длина запроса

// Не отправляй запрос для очень коротких строк
if (debouncedInput.length < 2) {
  setResults([]);
  return;
}

Правильная оптимизация поиска - это комбинация debouncing, кэширования, отмены старых запросов и минимальной длины поиска. Так получаешь быстрый, отзывчивый интерфейс без перегруза серверных ресурсов.