The Difference Between Debounce and Throttle and How to Use Them
In web applications, debounce and throttle are commonly used to prevent events from firing too frequently.
Although they may seem similar at first glance, they work differently and serve different purposes.
This article covers their definitions, key differences, typical use cases, and how to implement and use them in React.
Debounce vs Throttle – Key Differences
| Aspect | Debounce | Throttle |
|---|---|---|
| Behavior | Waits for a quiet period after the last event, then executes once | Executes at most once every fixed time interval |
| Main Purpose | Prevent excessive calls from rapid successive events | Limit the rate of frequently occurring events |
| Typical Use Cases | Search autocomplete, preventing duplicate button clicks | Scroll events, window resize events |
What is Debounce?
Debounce is a technique that ensures a function is only executed after a certain amount of time has passed without any new events being triggered. It is especially useful when users can trigger many events in a short time (typing, rapid clicking, etc.).
Common Debounce Use Cases:
- Search autocomplete: Instead of sending an API request on every keystroke, wait until the user stops typing for a short period.
- Prevent double-click / spam clicks: Ignore rapid successive clicks on the same button.
What is Throttle?
Throttle ensures that a function is executed at most once every specified time interval, no matter how many times the event is triggered during that period. It is ideal for performance optimization with high-frequency events.
Common Throttle Use Cases:
- Scroll event handling: Update UI or fetch data only every few hundred milliseconds while scrolling.
- Window resize events: Avoid recalculating layout or running expensive operations on every pixel change.
Code Implementation
This article uses React hook-style implementations (useDebounce and useThrottle) rather than plain utility functions.
import { useState, useEffect, useRef } from "react";
// Debounce Hook
export const useDebounce = <T,>(value: T, delay: number = 500): T => {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
};
// Throttle Hook
export const useThrottle = <T,>(value: T, delay: number = 500): T => {
const [throttledValue, setThrottledValue] = useState<T>(value);
const lastRan = useRef<number>(Date.now());
useEffect(() => {
const handler = setTimeout(() => {
if (Date.now() - lastRan.current >= delay) {
setThrottledValue(value);
lastRan.current = Date.now();
}
}, delay - (Date.now() - lastRan.current));
return () => clearTimeout(handler);
}, [value, delay]);
return throttledValue;
};
Debounce Example (Search Input)
import { useState, useEffect, ChangeEvent } from "react";
import { useDebounce } from "./utils";
export default function SimpleDebounceExample() {
const [inputValue, setInputValue] = useState<string>("");
const debouncedValue = useDebounce<string>(inputValue, 500);
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
};
useEffect(() => {
console.log(`Debounced Value: ${debouncedValue}`);
// Here you would usually trigger API call or heavy computation
}, [debouncedValue]);
return (
<div style={{ padding: "20px" }}>
<h1>Simple Debounce Example</h1>
<div style={{ marginBottom: "20px" }}>
<label htmlFor="search">Text input:</label>
<input
id="search"
type="text"
value={inputValue}
onChange={handleChange}
placeholder="Type something..."
/>
</div>
<div>
<p>Debounced Value: {debouncedValue}</p>
</div>
</div>
);
}
Throttle Example (Scroll Position)
import { useState, useEffect } from "react";
import { useThrottle } from "./utils";
export default function ScrollThrottleExample() {
const [scrollPosition, setScrollPosition] = useState < number > 0;
const throttledScrollPosition = useThrottle < number > (scrollPosition, 1000);
useEffect(() => {
const handleScroll = () => {
setScrollPosition(window.scrollY);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<div style={{ height: "200vh", padding: "20px" }}>
<h1>Scroll Event with Throttle Example</h1>
<p>Current scroll position: {throttledScrollPosition}px</p>
</div>
);
}
Plain Functions (throttleHelper) vs React Hooks (useThrottle) – Comparison
| Aspect | Plain function (throttleHelper / debounceHelper) | React Hook (useThrottle / useDebounce) |
|---|---|---|
| Usage environment | Can be used anywhere (React or non-React) | Only inside React components |
| Code complexity | Simple and concise | Slightly more complex (hooks + state) |
| State integration | Harder to connect with React state | Naturally integrates with React state |
| Lifecycle control | No built-in lifecycle management | Controlled via useEffect cleanup |
| Memoization | Must handle memoization manually | Easy to optimize with useCallback, useRef, etc. |
| Reusability | Framework-agnostic | React-specific |
Related Posts in Series
Collapse- 1. React Hooks - useRef
- 2. The Difference Between Debounce and Throttle and How to Use Them