
디바운스는 입력 값이 빠르게 연속적으로 변할 때, 마지막 입력만을 처리하기 위해 사d용한다.
예를 들어, 사용자가 검색창에 입력할 때마다 검색 요청을 보내는 대신, 입력이 멈추고 일정 시간이 지나면 그때 한 번만 요청을 보내는 것이 효율적이다.
이 훅은 사용자가 입력하는 값을 받아, 일정 시간(delay)이 지나기 전까지는 값을 변경하지 않다가, 시간이 지나면 그때서야 값을 업데이트한다.
import { useEffect, useState } from "react"
export const useDebounce = (value, delay) => {
const [devouncedValue, setDevouncedValue] = useState(value)
useEffect(() => {
const handler = setTimeout(() => {
setDevouncedValue(value)
}, delay)
return () => {
clearTimeout(handler)
}
//useEffect 안에서 return되는 함수는 컴포넌트가 리렌더링될 때마다
//기존의 타이머를 취소해주는 역할을 합니다. 이렇게 하면 이전 타이머가 계속
//남아 있지 않고, 최신 value에 대해서만 타이머가 동작하게 됩니다.
}, [value, delay])
//딜레이가 끝나기전에 종속성배열에따라 useEffect가 다시되면 딜레이시간이
//다시 초기화되서 시작
return devouncedValue
}
사용자가 검색창에 입력을 하면, 입력값은 onChange 이벤트 핸들러를 통해 searchTerm 상태로 업데이트된다.
이 부분에서 입력한 검색어가 searchTerm에 바로 반영
<form className="relative flex justify-center items-center w-[20.5rem] h-6 rounded-lg m-auto">
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} // searchTerm 업데이트
className="text-xs w-[20.5rem] bg-[hsl(214,13%,47%)] h-6 px-2 py-1 rounded-lg text-gray-300 text-center"
/>
<button
type="submit"
className="text-xs bg-slate-900 text-slate-300 w-[2.5rem] h-6 px-2 py-1 rounded-r-lg text-center absolute right-0 hover:bg-slate-700"
>
검색
</button>
</form>
onChange 핸들러가 호출될 때마다 setSearchTerm(e.target.value)가 실행되어 searchTerm 상태가 변경된다.
searchTerm이 변경될 때마다, useDebounce 훅이 실행되어, 입력이 멈추고 500ms 후에만 debouncedSearchTerm이 갱신된다.
사용자가 입력을 계속하는 동안 API 요청이 중복 발생하는 것을 방지하기 위한 것
debouncedSearchTerm은 searchTerm과는 달리, 일정 시간이 지난 후에만 최신 값을 유지합니다.
const debouncedSearchTerm = useDebounce(searchTerm, 500);
debouncedSearchTerm이 변경될 때마다, useEffect가 호출된다
사용자가 500ms 동안 입력을 중지하면 그제서야 debouncedSearchTerm 값이 갱신되고, 이때 API 요청을 트리거하는 함수인 handleSearchInput이 호출
useEffect(() => {
handleSearchInput(debouncedSearchTerm); // debouncedSearchTerm을 사용하여 API 호출
}, [debouncedSearchTerm]);
handleSearchInput은 debouncedSearchTerm을 받아서 그 값이 유효한 경우 포켓몬 API에 요청을 보낸다.
요청 성공 시 포켓몬 데이터를 받아오고, 실패 시에는 빈 배열로 설정하여 "포켓몬이 없습니다."라는 메시지를 표시
만약 debouncedSearchTerm이 빈 문자열이면, 처음 포켓몬 목록을 다시 가져오게 됩니다.
const handleSearchInput = async (searchTerm) => {
if (searchTerm.length > 0) {
try {
const response = await axios.get(`https://pokeapi.co/api/v2/pokemon/${searchTerm}`);
const pokemonData = {
url: `https://pokeapi.co/api/v2/pokemon/${response.data.id}`,
name: searchTerm,
};
setPokemons([pokemonData]); // 검색된 포켓몬만 배열에 저장
} catch (error) {
setPokemons([]); // 에러 발생 시 포켓몬 데이터 초기화
console.error(error);
}
} else {
fetchPokeData(true); // 검색어가 없으면 기본 포켓몬 리스트 불러오기
}
};