Одна из замечательных особенностей 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)