Создание поисковой строки в React

Поисковые строки помогают пользователям быстро находить нужное на сайте и дают ценные сигналы для аналитики — какие термины и товары интересуют аудиторию. В этой статье показано, как создать простую поисковую строку в React, которая фильтрует и отображает данные по мере ввода. Также описаны варианты улучшения и критерии приёмки.
Краткое определение
useState — хук React для хранения локального состояния компонента. map — метод массива для создания нового массива из результатов функции. filter — метод массива для отбора элементов по условию.
Создание базовой поисковой строки
Поиск принимает ввод пользователя и запускает функцию фильтрации. Для форм в React можно использовать библиотеки вроде Formik, но для простого поиска достаточно сделать компонент с нуля.
Если у вас нет проекта React, создайте его командой:
npx create-react-app search-barВ файле App.js добавьте форму с input:
export default function App() {
return (
)
}Используйте хук useState и событие onChange для управления вводом:
import { useState } from "react"
export default function App() {
const [query, setQuery] = useState('')
const handleChange = (e) => {
setQuery(e.target.value)
}
return (
)
}Каждый ввод обновляет состояние через handleChange.
Фильтрация данных при вводе
Поисковая строка должна фильтровать массив данных по значению query. Храните и текст запроса, и результаты в состоянии:
const [state, setState] = useState({
query: '',
list: []
})Объединение значений в один объект состояния уменьшает число перерисовок.
Пример данных (список постов):
const posts = [
{
url: '',
tags: ['react', 'blog'],
title: 'How to create a react search bar',
},
{
url:'',
tags: ['node','express'],
title: 'How to mock api data in Node',
},
// more data here
]Далее — фильтрация внутри handleChange:
const handleChange = (e) => {
const results = posts.filter(post => {
if (e.target.value === "") return posts
return post.title.toLowerCase().includes(e.target.value.toLowerCase())
})
setState({
query: e.target.value,
list: results
})
}Если строка поиска пустая, функция возвращает все посты. Сравнение в нижнем регистре делает поиск нечувствительным к регистру.
Отображение результатов
Отобразите результаты, пройдя по массиву list методом map:
export default function App() {
// state and handleChange() function
return (
{(state.query === '' ? "" : state.list.map(post => {
return - {post.title}
}))}
)
}Если вы не хотите скрывать список по умолчанию, уберите проверку на пустой query.
Добавьте сообщение, когда ничего не найдено — это улучшает UX:
{(state.query === '' ? "No posts match the query" : !state.list.length ? "Your query did not return any results" : state.list.map(post => {
return - {post.title}
}))}
Поиск по нескольким полям
Фильтрация по одному полю (title) работает для простых случаев. Чтобы расширить функциональность, объедините несколько проверок через логическое ИЛИ:
const results = posts.filter(post => {
const q = e.target.value.toLowerCase()
return post.title.toLowerCase().includes(q) || post.tags.join(' ').toLowerCase().includes(q)
})Так поиск найдёт совпадения и в теге, и в имени.
Производительность и масштабирование
Важно учитывать объём данных и частоту запросов. Клиентская фильтрация удобна для небольших наборов (несколько сотен — пару тысяч записей). Для больших списков используйте серверный поиск или инкрементную загрузку.
Рекомендации по оптимизации:
- Debounce ввода: добавьте задержку перед фильтрацией, чтобы не запускать её на каждый символ.
- useMemo: мемоизируйте результаты фильтрации, чтобы не пересчитывать их при незначительных изменениях.
- Пагинация/виртуализация: показывайте и рендерьте только видимую часть списка (react-window/react-virtualized).
Пример простого debounce-хука:
import { useState, useEffect } from 'react'
export function useDebounce(value, delay = 300) {
const [debounced, setDebounced] = useState(value)
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay)
return () => clearTimeout(id)
}, [value, delay])
return debounced
}Использование:
const debouncedQuery = useDebounce(state.query, 250)
// фильтровать по debouncedQuery вместо state.queryАльтернативные подходы
- Серверный поиск: выполняйте фильтрацию на сервере, особенно при больших данных или необходимости полнотекстового поиска.
- Fuzzy-поиск: библиотеки вроде Fuse.js поддерживают нечёткий поиск и ранжирование результатов.
- Индексация: ElasticSearch, Algolia — для сложных и масштабируемых сценариев.
Когда клиентская фильтрация не подходит
- Нужна сложная логика ранжирования или синонимы.
- Данные слишком большие для загрузки на клиент.
- Нужно учитывать права доступа на уровне записей.
Критерии приёмки
- Поиск возвращает результаты при вводе известных частей заголовка.
- Поиск нечувствителен к регистру.
- При пустом запросе отображается либо всё, либо понятное сообщение (в зависимости от продукта).
- При отсутствии совпадений показывается сообщение «По вашему запросу ничего не найдено».
- Время ответа для интерактивного поиска — менее 300 мс на клиенте после дебаунса (целевое поведение, а не строгое измерение).
Чек-листы по ролям
Разработчик:
- Реализовал дебаунс и мемоизацию.
- Добавил обработку ошибок и проверку типов данных.
- Написал unit-/integration-тесты для фильтрации.
QA:
- Проверил кейсы с пустым запросом, спецсимволами, длинными строками.
- Проверил поведение при медленном соединении и отказе API (если применяется серверный поиск).
Продуктовый менеджер:
- Подтвердил требования по UX при отсутствии результатов.
- Определил поля, по которым должен действовать поиск.
Тестовые сценарии
- Ввод полного названия возвращает соответствующий пост.
- Ввод части слова возвращает посты с этим фрагментом.
- Ввод в разном регистре — результаты те же.
- Ввод спецсимволов — система не падает и корректно фильтрует.
- Быстрый набор символов — фильтрация запускается после дебаунса.
Безопасность и приватность
Не включайте в клиентский поиск чувствительные данные. Если поиск по данным с ограниченным доступом, реализуйте проверку прав на сервере.
Мини-методология внедрения
- Реализовать базовую фильтрацию и отрисовку результатов.
- Добавить debounce и useMemo для производительности.
- Провести нагрузочное тестирование на реальном объёме данных.
- При необходимости переключить на серверный поиск или добавить Fuse.js.
Краткий глоссарий
- Debounce — техника задержки выполнения функции до остановки серии событий.
- useMemo — хук для мемоизации вычислений в React.
- Fuzzy-поиск — нечёткий поиск, учитывающий опечатки и похожие слова.
Итог
Поисковая строка в React — это сочетание простого состояния и методов массива. Для производства важно продумать объём данных, UX при отсутствии результатов и оптимизации (debounce, мемоизация, виртуализация). Если объём данных или требования к ранжированию растут, рассматривайте серверный поиск или специализированные решения.
Важно: тестируйте поведение на реальном наборе данных и предусмотрите сообщения для случаев, когда результатов нет.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone