DEV Community

Mukhammad Sobirov
Mukhammad Sobirov

Posted on

Кастомный React хук— useDebounce

Одна из замечательных особенностей React заключается в том, что он позволяет создавать собственные хуки и предлагать более эффективные способы решения проблем. Понимание того, как создать собственный хук, является ценным навыком разработчика. Есть много отличных кастомных хуков, которые «стандартизированы» в сообществе разработчиков React, и useDebounce() — один из них.

В этой статье мы собираемся создать мини-приложение, которое позволит пользователям искать изображения из Unsplash API. Я также предоставлю ссылку на репозиторий Github.

Создание акаунта в Unsplash

  1. Сначала нам нужно зарегистрироваться в Unsplash
  2. Далее нам нужно создать там новое приложение

создание акканта в unsplash

важно отметить, что мы будем использовать их демонстрационное приложение, которое имеет ограничение в 50 запросов в час. Для готовых к производству приложений проверьте их требования на той же странице. Также ознакомьтесь с их рекомендациями по использованию API.

  1. Наконец, обязательно скопируйте ключ доступа и секретные ключи, которые они предоставляют.

Создание приложения

Просто создайте минимальное React приложение. Я использую Vite.

npm create vite@latest
Enter fullscreen mode Exit fullscreen mode

Выберите простой шаблон React на основе JavaScript и позвольте ему настроить проект. После завершения настройки нажмите npm i.

Когда все будет готово, создайте файл .env в корневой папке проекта.

создание env файла

И добавьте ключи доступа и секретные ключи, как показано ниже. Если вы используете Vite, важно начинать имя ключа с «VITE».

VITE_ACCESS_KEY="your key"
VITE_SECRET_KEY="your key"
Enter fullscreen mode Exit fullscreen mode

Затем перейдите к файлу App.jsx и удалите оттуда лишний код, как показано ниже:

import { useState } from "react";
import "./App.css";

function App() {
  return <div className="App"></div>;
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Мы собираемся добавить сюда простой компонент search, чтобы пользователь мог ввести слово для поиска.

import { useState } from "react";
import "./App.css";

function App() {
  const [searchTerm, setSearchTerm] = useState("");

  return (
    <div className="App">
      <div>
        <label htmlFor="search-input">Search Unsplash images</label>
        <input type="search" id="search-input" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
      </div>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Идея в том, что мы отправляем запрос в Unsplash API каждый раз, когда пользователь обновляет слово поиска. Простым способом реализации этой логики было бы использование useEffect с searchTerm в массиве зависимостей.

import { useState, useEffect } from "react";
import "./App.css";

function App() {
  const [searchTerm, setSearchTerm] = useState("");

  const handleSearch = () => {
    console.log("searching for", searchTerm);
  };

  useEffect(() => {
    handleSearch();
  }, [searchTerm]);

  return (
    <div className="App">
      <div>
        <label htmlFor="search-input">Search Unsplash images</label>
        <input type="search" id="search-input" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
      </div>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Можете ли вы угадать проблему с этим подходом? Откроем консоль браузера и проверим, что там происходит:

Image description

Помните, что у нас есть ограничение в 50 запросов в час. Даже если бы у нас было больше, отправка запроса в API при каждом вводе пользователем — не очень масштабируемая и дорогая операция. В идеале вы хотели бы дождаться, пока пользователь введет слово целиком, а затем отправить запрос. Вот где в игру вступает useDebounce! так что давайте реализуем это дальше.

useDebounce()

Давайте создадим файл с именем useDebounce:

useDebounce хук

Внутри файла мы собираемся написать сам хук. Идея в том, что мы хотим предотвратить быстрое изменение состояния searchTerm . Этот хук принимает два входных параметра — значение, которое должно “отскочить”, и задержка в миллисекундах (обычно 500). При объединении useDebounce с useEffect вы можете быть уверены, что будет отражено только самое последнее значение, поскольку хук не будет вызываться в течение указанного времени. Вот код:

function useDebounce(value, delay) {
  // value and delay in ms (1000ms = 1s)
  // debounced values
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(
    () => {
      // Update debounced value after delay
      const t = setTimeout(() => {
        setDebouncedValue(value);
      }, delay);

      // clean up the timeout after value changes
      return () => {
        clearTimeout(t);
      };
    },
    [value, delay] // re-run if value or delay changes
  );
  return debouncedValue;
}

export default useDebounce;
Enter fullscreen mode Exit fullscreen mode

Наконец, давайте объединим это с нашей существующей логикой и сравним с предыдущим поведением:

import { useState, useEffect } from "react";
import "./App.css";
import useDebounce from "./useDebounce";

function App() {
  const [searchTerm, setSearchTerm] = useState("");
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  const handleSearch = () => {
    console.log("searching for", searchTerm);
  };

  useEffect(() => {
    if (debouncedSearchTerm) {
      handleSearch();
    }
  }, [debouncedSearchTerm]);

  return (
    <div className="App">
      <div>
        <label htmlFor="search-input">Search Unsplash images</label>
        <input type="search" id="search-input" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
      </div>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Разве это не безумие, насколько эффективной стала наша поисковая логика?

демонстрация

Unsplash интеграция

Теперь с этим кодом мы можем безопасно интегрировать поиск Unsplash.

import { useState, useEffect } from "react";
import "./App.css";
import useDebounce from "./useDebounce";

const API_URL = "https://api.unsplash.com/search/photos";

function App() {
  const [searchTerm, setSearchTerm] = useState("");
  const [results, setResults] = useState([]);

  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  const handleSearch = async () => {
    const response = await fetch(
      `${API_URL}?query=${debouncedSearchTerm}&client_id=${import.meta.env.VITE_ACCESS_KEY}`
    );
    const data = await response.json();
    return data.results;
  };

  useEffect(() => {
    if (debouncedSearchTerm) {
      handleSearch().then((results) => setResults(results));
    }
  }, [debouncedSearchTerm]);

  return (
    <div className="App">
      <div>
        <label htmlFor="search-input">Search Unsplash images</label>
        <input type="search" id="search-input" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
      </div>
      <div>
        {results?.map((result) => (
          <img key={result.id} src={result.urls.small} alt={result.alt_description} />
        ))}
      </div>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

финальное приложение

Вот и все

Я надеюсь, что вы нашли эту статью/туториал полезным. Я рекомендую вам попробовать useDebounce в своих проектах и создавать высококачественные приложения. Вот ссылка на репозиторий Github.

Top comments (0)