useDebouncedCallback
ํจ์๋ฅผ ๋๋ฐ์ด์คํ์ฌ ๋ฐํํ๋ Hook์
๋๋ค. leading, trailing, maxWait ์ต์
์ ์ง์ํฉ๋๋ค.
์ธ์ ์ฌ์ฉํ๋์?
- ๐ ๊ฒ์ API ํธ์ถ
- ๐ฑ๏ธ ๋ฒํผ ์ฐํ ๋ฐฉ์ง
- ๐พ ์๋ ์ ์ฅ (ํ์ดํ ์ค)
- ๐ ์คํฌ๋กค ์ด๋ฒคํธ ์ต์ ํ
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
import { useDebouncedCallback } from '@frontend-toolkit-js/hooks';
function SearchInput() {
const debouncedSearch = useDebouncedCallback((query: string) => {
fetch(`/api/search?q=${query}`);
}, 300);
return (
<input
onChange={e => debouncedSearch(e.target.value)}
placeholder="๊ฒ์..."
/>
);
}์ฃผ์ ์ฌ์ฉ ์ฌ๋ก
1. ๊ฒ์ ์๋์์ฑ
function AutocompleteSearch() {
const [results, setResults] = useState([]);
const debouncedSearch = useDebouncedCallback(async (query: string) => {
const data = await fetch(`/api/search?q=${query}`);
setResults(data);
}, 300);
return <input onChange={e => debouncedSearch(e.target.value)} />;
}2. ๋ฒํผ ์ฐํ ๋ฐฉ์ง (leading)
function PaymentForm() {
const handleSubmit = useDebouncedCallback(
async formData => {
await submitPayment(formData);
},
2000,
{ leading: true, trailing: false }
);
return <button onClick={handleSubmit}>๊ฒฐ์ ํ๊ธฐ</button>;
}๋์:
ํด๋ฆญ1 (0ms) โ ์ฆ์ ์คํ โ
ํด๋ฆญ2 (500ms) โ ๋ฌด์ โ
ํด๋ฆญ3 (1000ms) โ ๋ฌด์ โ
ํด๋ฆญ4 (2100ms) โ ๋ค์ ์ฆ์ ์คํ โ
3. ์๋ ์ ์ฅ (maxWait)
function Editor() {
const [content, setContent] = useState('');
const autoSave = useDebouncedCallback(
(text: string) => {
saveToServer(text);
},
1000,
{ maxWait: 5000 }
);
return (
<textarea
value={content}
onChange={e => {
setContent(e.target.value);
autoSave(e.target.value);
}}
/>
);
}๋์:
๊ณ์ ํ์ดํ โ 1์ด ํ์ด๋จธ ๊ณ์ ๋ฆฌ์
5์ด ๊ฒฝ๊ณผ โ ๊ฐ์ ์ ์ฅ! โก
ํ์ดํ ๋ฉ์ถค โ 1์ด ํ ์ ์ฅ โ
4. ์คํฌ๋กค ํธ๋ํน (leading + trailing)
function ScrollTracker() {
const trackScroll = useDebouncedCallback(
() => {
analytics.track('scroll', { position: window.scrollY });
},
200,
{ leading: true, trailing: true }
);
useEffect(() => {
window.addEventListener('scroll', trackScroll);
return () => window.removeEventListener('scroll', trackScroll);
}, [trackScroll]);
return <div>...</div>;
}๋์:
์คํฌ๋กค ์์ โ ์ฆ์ ์ถ์ โ
(leading)
๊ณ์ ์คํฌ๋กค โ ๋๊ธฐ...
์คํฌ๋กค ๋ฉ์ถค โ 200ms ํ ์ถ์ โ
(trailing)API Reference
function useDebouncedCallback<T extends (...args: any[]) => any>(
callback: T,
delay: number,
options?: DebouncedCallbackOptions
): T;Parameters
callback
๋๋ฐ์ด์คํ ํจ์์ ๋๋ค.
- ํ์
:
(...args: any[]) => any
delay
์ง์ฐ ์๊ฐ(๋ฐ๋ฆฌ์ด)์ ๋๋ค.
- ํ์
:
number
options
๋๋ฐ์ด์ค ์ต์ ์ ๋๋ค.
interface DebouncedCallbackOptions {
leading?: boolean; // ์ฒซ ํธ์ถ ์ฆ์ ์คํ
trailing?: boolean; // ๋ง์ง๋ง ํธ์ถ ํ ์คํ
maxWait?: number; // ์ต๋ ๋๊ธฐ ์๊ฐ
}์ต์ ์์ธ
leading
์ฒซ ํธ์ถ ์ ์ฆ์ ์คํ ์ฌ๋ถ
// leading: true โ ์ฒซ ํธ์ถ ์ฆ์ ์คํ
const debounced = useDebouncedCallback(fn, 1000, {
leading: true,
trailing: false,
});
// ์ฌ์ฉ ์ฌ๋ก: ๋ฒํผ ์ฐํ ๋ฐฉ์ง, ํผ ์ ์ถ- ๊ธฐ๋ณธ๊ฐ:
false
trailing
๋ง์ง๋ง ํธ์ถ ํ ์ง์ฐ ์๊ฐ ๋ค ์คํ ์ฌ๋ถ
// trailing: true (๊ธฐ๋ณธ) โ ๋ง์ง๋ง ํธ์ถ ํ ์คํ
const debounced = useDebouncedCallback(fn, 300);
// ์ฌ์ฉ ์ฌ๋ก: ๊ฒ์ ์๋์์ฑ, ์
๋ ฅ ๊ฒ์ฆ- ๊ธฐ๋ณธ๊ฐ:
true
maxWait
์ต๋ ๋๊ธฐ ์๊ฐ. ์ด ์๊ฐ์ด ์ง๋๋ฉด ๊ฐ์ ๋ก ์คํ๋ฉ๋๋ค.
// maxWait: 5000 โ ์ต๋ 5์ด๋ง๋ค ๊ฐ์ ์คํ
const debounced = useDebouncedCallback(save, 1000, { maxWait: 5000 });
// ์ฌ์ฉ ์ฌ๋ก: ์๋ ์ ์ฅ, ์ฃผ๊ธฐ์ ๋๊ธฐํ- ๊ธฐ๋ณธ๊ฐ:
undefined
์ต์ ์กฐํฉ
๊ธฐ๋ณธ (Trailing๋ง)
useDebouncedCallback(fn, 300);
// = { leading: false, trailing: true }
// ์
๋ ฅ ๋ฉ์ถ ํ 300ms ํ ์คํLeading๋ง
useDebouncedCallback(fn, 1000, {
leading: true,
trailing: false,
});
// ์ฒซ ํธ์ถ๋ง ์ฆ์ ์คํ, ์ดํ 1์ด๊ฐ ๋ฌด์Leading + Trailing
useDebouncedCallback(fn, 200, {
leading: true,
trailing: true,
});
// ์์ ์ ์ฆ์ ์คํ + ๋๋ ๋๋ ์คํTrailing + MaxWait
useDebouncedCallback(fn, 1000, {
trailing: true,
maxWait: 5000,
});
// ์ผ๋ฐ: 1์ด ๋๊ธฐ, ๊ฐ์ : 5์ด๋ง๋ค ์คํ์ฑ๋ฅ ํน์ฑ
๋ฒ๋ค ํฌ๊ธฐ
- ~1.2 KB (minified)
- ~0.5 KB (gzipped)
๋ฉ๋ชจ๋ฆฌ
- ํ์ด๋จธ ์๋ ์ ๋ฆฌ (๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง)
- cleanup ํจ์๋ก ์ธ๋ง์ดํธ ์ ์ ๋ฆฌ
vs useDebounce
| ํน์ง | useDebounce | useDebouncedCallback |
|---|---|---|
| ๋๋ฐ์ด์ค ๋์ | ๊ฐ | ํจ์ |
| ๋ฐํ | ๊ฐ | ํจ์ |
| ์ต์ | โ | โ leading, trailing, maxWait |
| ์ฌ์ฉ ์ฌ๋ก | ๊ฒ์์ด | ์ด๋ฒคํธ ํธ๋ค๋ฌ |
// useDebounce: ๊ฐ ๋๋ฐ์ด์ค
const debouncedValue = useDebounce(value, 300);
// useDebouncedCallback: ํจ์ ๋๋ฐ์ด์ค
const debouncedFn = useDebouncedCallback(fn, 300);TypeScript
ํ์ ์ด ์๋์ผ๋ก ์ถ๋ก ๋ฉ๋๋ค.
// โ
ํ์
์ถ๋ก
const debouncedSearch = useDebouncedCallback((query: string, limit: number) => {
fetch(`/api?q=${query}&limit=${limit}`);
}, 300);
debouncedSearch('hello', 10); // โ
debouncedSearch('hello'); // โ ์๋ฌ: limit ํ์
debouncedSearch(123, 10); // โ ์๋ฌ: query๋ string์ฃผ์์ฌํญ
โ ๏ธ ํด๋ฆฐ์
์ปดํฌ๋ํธ ์ธ๋ง์ดํธ ์ ์๋์ผ๋ก ํ์ด๋จธ๊ฐ ์ ๋ฆฌ๋ฉ๋๋ค.
// ์๋ cleanup๋จ - ๋ณ๋ ์ฒ๋ฆฌ ๋ถํ์
const debounced = useDebouncedCallback(fn, 300);๊ด๋ จ ๋ฌธ์
- useDebounce - ๊ฐ ๋๋ฐ์ด์ค
- useThrottle - ์ฐ๋กํ๋ง (๊ฐ๋ฐ ์์ )