Одна из замечательных особенностей React заключается в том, что он позволяет создавать собственные хуки и предлагать более эффективные способы решения проблем. Понимание того, как создать собственный хук, является ценным навыком разработчика. Есть много отличных кастомных хуков, которые «стандартизированы» в сообществе разработчиков React, и useDebounce()
— один из них.
В этой статье мы собираемся создать мини-приложение, которое позволит пользователям искать изображения из Unsplash API. Я также предоставлю ссылку на репозиторий Github.
Создание акаунта в Unsplash
- Сначала нам нужно зарегистрироваться в Unsplash
- Далее нам нужно создать там новое приложение
важно отметить, что мы будем использовать их демонстрационное приложение, которое имеет ограничение в 50 запросов в час. Для готовых к производству приложений проверьте их требования на той же странице. Также ознакомьтесь с их рекомендациями по использованию API.
- Наконец, обязательно скопируйте ключ доступа и секретные ключи, которые они предоставляют.
Создание приложения
Просто создайте минимальное React приложение. Я использую Vite.
npm create vite@latest
Выберите простой шаблон React на основе JavaScript и позвольте ему настроить проект. После завершения настройки нажмите npm i
.
Когда все будет готово, создайте файл .env в корневой папке проекта.
И добавьте ключи доступа и секретные ключи, как показано ниже. Если вы используете Vite, важно начинать имя ключа с «VITE».
VITE_ACCESS_KEY="your key" VITE_SECRET_KEY="your key"
Затем перейдите к файлу App.jsx и удалите оттуда лишний код, как показано ниже:
import { useState } from "react"; import "./App.css"; function App() { return <div className="App"></div>; } export default App;
Мы собираемся добавить сюда простой компонент 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;
Идея в том, что мы отправляем запрос в 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;
Можете ли вы угадать проблему с этим подходом? Откроем консоль браузера и проверим, что там происходит:
Помните, что у нас есть ограничение в 50 запросов в час. Даже если бы у нас было больше, отправка запроса в API при каждом вводе пользователем — не очень масштабируемая и дорогая операция. В идеале вы хотели бы дождаться, пока пользователь введет слово целиком, а затем отправить запрос. Вот где в игру вступает 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;
Наконец, давайте объединим это с нашей существующей логикой и сравним с предыдущим поведением:
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;
Разве это не безумие, насколько эффективной стала наша поисковая логика?
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;
Вот и все
Я надеюсь, что вы нашли эту статью/туториал полезным. Я рекомендую вам попробовать useDebounce
в своих проектах и создавать высококачественные приложения. Вот ссылка на репозиторий Github.
Top comments (0)