В этой статье будет продемонстрировано, как реализовать функцию поиска по полученному набору данных. Эта функция позволит нам искать все символы, доступные через API. Примечание. Эта статья является продолжением статьи Извлечение данных с помощью apollo-client в React из GraphQL API.

Что мы строим.

Поведение, которого мы хотим добиться, заключается в том, что когда пользователь вводит имя персонажа в поле поиска и нажимает кнопку «Отправить», этот символ отображается на веб-странице.

Инициализировать состояние

Поведение, которое мы хотим реализовать, заключается в том, что если элемент ввода пуст, он должен отображать все символы. Если в строке поиска есть текст, в ней должны отображаться только символы, включающие эту текстовую строку.

Теперь давайте объявим переменную состояния для хранения поискового запроса — символов, которые мы ищем. В компоненте App вызовите useState(), чтобы объявить переменную состояния с именем searchInput и функцию обновления состояния с именем setSearchInput. установитьSearchQuery. Передайте значение useState() a в качестве начального состояния запроса:

const [searchInput, setSearchInput] = useState("");

Здесь значение, с которого мы хотим начать, представляет собой пустую строку. Состояние searchInput будет обновлено значением, которое пользователь вводит в поле ввода поиска.

В компоненте App создайте функцию с именем handleSearch, которая обновляет состояние searchInput, когда пользователь отправляет значение. Событие, которое функцияhandleSearch получает в качестве параметра, представляет собой объект, содержащий поле target. Это target — это элемент DOM, к которому привязан обработчик событий (т. е. поле ввода текста). Получив доступ к этому полю, мы можем определить, на что будет изменено значение target:

const handleSearch = e => {
  const searchQuery = e.target.value && e.target.value.toLowerCase();
  console.log(searchQuery)
  setSearchInput(searchQuery)
};

Теперь нам нужно подключить функцию handleSearch к компоненту SearchBar. В файле App.js импортируйте и вставьте компонент SearchBar непосредственно внутри элемента .container:

import SearchBar from './SearchBar'

return (
  <div className="container">
    <SearchBar />
  </div>
)

Дайте компоненту SearchBar свойство handleSearch, передав ему ссылку на функцию handleSearch и свойство searchInput, передав ему ссылку на значение searchInput:

const App = () => {
  const [searchInput, setSearchInput] = useState("");

  const handleSearch = e => {
    const searchQuery = e.target.value && e.target.value.toLowerCase();
    console.log(searchQuery)
    setSearchQuery(searchQuery)
  };

  return (
    <div className="container">
      <SearchBar 
        searchInput={searchInput}
        handleSearch={handleSearch} 
      />

    </div>
  );

}

Логика поиска

Мы используем перехватчик useEffect() для определения логики, которая возвращает список символов для результатов поиска после рендеринга компонента. Напомним, хук useEffect() принимает два аргумента: функцию и массив зависимостей.

Переменная searchResults фильтрует список символов, затем принимает имя символа и преобразует его в нижний регистр с помощью метода toLowerCase. Затем использует метод includes, чтобы определить, содержит ли имя персонажа searchQuery .

  useEffect(() => {
    const searchResults = data?.characters?.results.filter(character => character.name.toLowerCase().includes(searchQuery))
    setSearchedCharacters(searchResults)
  }, [searchQuery, data])

Наконец, если зависимость не указана, эффект запускается после каждого рендеринга. Если он пуст, то он запустится один раз. Однако он должен содержать список значений, используемых в эффекте. Эффект запускается после изменения любого из этих значений. В данном случае мы передаем searchQuery, поскольку он меняется при каждом поиске пользователя, и data, поскольку он возвращает разные результаты метода фильтра.

Создать компонент панели поиска

Теперь мы фактически создадим компонент SearchBar, который мы ранее поместили в компонент App:

$ touch src/SearchBar.js

В файл SearchBar.js импортируйте хук useState:

import { useState } from 'react';

Создайте функциональный компонент и скопируйте фрагмент кода для Bootstrap NavBar — удалите элементы form и button:

const SearchBar = () => {
    return(
      <nav class="navbar navbar-expand-lg bg-body-tertiary">
        <div class="container-fluid">
          <a class="navbar-brand" href="https://rickandmortyapi.com/documentation/">Rick and Morty</a>
          <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
        </div>
      </nav>
    )
}

export default SearchBar;

Передайте параметры searchInput и handleSearch в функцию SearchBar. Обратите внимание: мы извлекли значения из объекта props (то есть деструктуризации), что дает нам возможность использовать props без необходимости повторять это слово по всему коду. Напомним, они были добавлены в качестве реквизита к компоненту SearchBar в файле App.js.

const SearchBar = ({searchInput, handleSearch}) => {
    return(
      <nav class="navbar navbar-expand-lg bg-body-tertiary">
        <div class="container-fluid">
          <a class="navbar-brand" href="https://rickandmortyapi.com/documentation/">Rick and Morty</a>
          <input 
            class="form-control me-2" 
            type="search" 
            placeholder="Search" 
            aria-label="Search">
        </div>
      </nav>
    )
}

export default SearchBar;

Теперь searchInput и handleSearch доступны в качестве реквизитов для передачи элементу ввода. Используйте обработчик onChange для прослушивания любых изменений ввода и запуска события при изменении значения ввода.

Значением свойства является функция handleChange, обработчик событий, определенный ранее в статье.

const SearchBar = ({searchInput, handleSearch}) => {
    return(
      <nav class="navbar navbar-expand-lg bg-body-tertiary">
        <div class="container-fluid">
          <a class="navbar-brand" href="https://rickandmortyapi.com/documentation/">Rick and Morty</a>
          <input 
            class="form-control me-2"
            defaultValue={searchInput} 
            onChange={handleChange} 
            type="search" 
            placeholder="Search characters" 
            aria-label="Search">
        </div>
      </nav>
    )
}

export default SearchBar;

CSS-стиль

Давайте добавим те же цвета и шрифты, что и в предыдущих статьях. В дополнение к стандартным цветам темы Bootstrap давайте добавим собственные зеленый и черный. Давайте применим эти цвета к body веб-страницы и border каждой карточки персонажа.

@import "../node_modules/bootstrap/scss/bootstrap.scss";

$neonGreen: #C3ED3F;
$softBlack: #141414;

body {
  background-color: $neonGreen;
}

.card {
  box-shadow: 5px 5px 1px 1px $softBlack;
  border: 2px solid $softBlack;
  border-radius: 8px;
}

Чтобы подчеркнуть бруталистский стиль веб-страницы, давайте используем для текста шрифт Google под названием Darker Grotesque. Выбранные семейства включают: Regular 400, Medium 500 и Bold 700.

Чтобы встроить выбранный шрифт, щелкните переключатель «Импорт» в правом столбце веб-страницы Darker Grotesque и вставьте ссылку на импорт в верхнюю часть файла custom.scss. Затем скопируйте правило CSS для font-family и вставьте его в селектор тела.

@import url('https://fonts.googleapis.com/css2?family=Darker+Grotesque:wght@400;500;700&display=swap');
@import "../node_modules/bootstrap/scss/bootstrap.scss";

$neonGreen: #C3ED3F;
$softBlack: #141414;

body {
  background-color: $neonGreen;
  font-family: 'Darker Grotesque', sans-serif;
}

Заключение

В этой статье показано, как реализовать функцию поиска по полученному набору данных — API «Рик и Морти».