Table of Contents
- Introduction
- What is Debouncing?
- What is Throttling?
- Comparing Debouncing vs Throttling
- Debouncing in React: Real Use Cases and Implementations
- 5.1 Debouncing with Lodash
- 5.2 Custom Debounce Hook
- Throttling in React: Real Use Cases and Implementations
- 6.1 Throttling with Lodash
- 6.2 Custom Throttle Hook
- Best Practices and Common Mistakes
- When to Use What?
- Conclusion
1. Introduction
React applications often handle user-driven, high-frequency events like typing, scrolling, and window resizing. Without control, these events can lead to:
- Excessive state updates
- Performance bottlenecks
- API rate-limiting issues
- Janky or unresponsive UI
To mitigate these issues, debouncing and throttling are used. Though similar in intent—to reduce frequency of execution—they differ in implementation and use cases.
2. What is Debouncing?
Debouncing ensures that a function is only invoked once the user has stopped triggering the event for a specified delay.
How it Works:
- A timer is set every time the event is fired.
- If the event is triggered again before the delay period ends, the previous timer is cleared.
- The function runs only once after the user pauses interaction.
Example Scenarios:
- Live search input
- Auto-saving form data
- Input validation
- Resize listener (when action is needed after resizing ends)
3. What is Throttling?
Throttling ensures a function is called at most once in every specified interval, regardless of how many times the event fires.
How it Works:
- A function runs immediately or after the first call.
- Subsequent calls are ignored until the defined interval has passed.
Example Scenarios:
- Scroll tracking (e.g., infinite scroll)
- Window resizing with frequent re-renders
- Mouse move listeners
4. Comparing Debouncing vs Throttling
Feature | Debouncing | Throttling |
---|---|---|
Execution timing | After user stops triggering event | At regular intervals |
Frequency | Once after a delay | Repeatedly at controlled intervals |
Use case | Typing/search input | Scroll, resize, drag, mouse move |
Complexity | Simpler behavior | Slightly more nuanced timing |
5. Debouncing in React: Real Use Cases and Implementations
5.1 Debouncing with Lodash
Install lodash if not already:
bashCopyEditnpm install lodash
Use it like this:
jsxCopyEditimport { debounce } from 'lodash';
import { useState } from 'react';
const SearchInput = () => {
const [query, setQuery] = useState('');
const debouncedSearch = debounce((value) => {
console.log("Searching for:", value);
// API Call here
}, 500);
const handleChange = (e) => {
const value = e.target.value;
setQuery(value);
debouncedSearch(value);
};
return <input type="text" value={query} onChange={handleChange} />;
};
5.2 Custom Debounce Hook
Avoiding lodash? You can build your own hook:
jsxCopyEditimport { useEffect, useState } from 'react';
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
Usage:
jsxCopyEditconst SearchInput = () => {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 500);
useEffect(() => {
if (debouncedQuery) {
console.log("Search:", debouncedQuery);
}
}, [debouncedQuery]);
return <input value={query} onChange={e => setQuery(e.target.value)} />;
};
6. Throttling in React: Real Use Cases and Implementations
6.1 Throttling with Lodash
jsxCopyEditimport { throttle } from 'lodash';
import { useEffect, useState } from 'react';
const ScrollTracker = () => {
const [scrollY, setScrollY] = useState(0);
const handleScroll = throttle(() => {
setScrollY(window.scrollY);
}, 200);
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return <div>Scroll Y: {scrollY}</div>;
};
6.2 Custom Throttle Hook
jsxCopyEditexport const useThrottle = (callback, delay) => {
const lastCall = useRef(0);
return (...args) => {
const now = Date.now();
if (now - lastCall.current >= delay) {
lastCall.current = now;
callback(...args);
}
};
};
Usage Example:
jsxCopyEditconst handleScroll = useThrottle(() => {
console.log('Throttled scroll event');
}, 300);
7. Best Practices and Common Mistakes
✅ Best Practices:
- Use
useCallback
oruseRef
when working with debounced/throttled functions to persist the reference. - Clean up your listeners on unmount.
- Always debounce API calls, not just the input event.
❌ Common Mistakes:
- Not memoizing the debounced/throttled function — leads to multiple timers being created.
- Forgetting to clear debounce/throttle effects on unmount — causes memory leaks.
- Using throttle for typing events (instead of debounce) — wastes resources and creates weird behavior.
8. When to Use What?
Scenario | Use Debounce | Use Throttle |
---|---|---|
Search bar | ✅ | ❌ |
Window resize listener | ✅ (post-event handling) / ✅ throttle for live updates | |
Scroll position tracking | ❌ | ✅ |
Mouse move handler | ❌ | ✅ |
Auto-save on typing | ✅ | ❌ |
9. Conclusion
Understanding debouncing and throttling can greatly improve your React application’s efficiency, UX, and performance—especially in complex, real-world use cases. Mastering these techniques allows developers to better manage event-based logic and optimize expensive renders or function calls.
To recap:
- Use debounce for user inputs where waiting for a pause makes sense.
- Use throttle for constant events like scrolling or resizing.
In upcoming modules, we’ll use both techniques in projects involving search filtering, infinite scroll, and dashboard UIs.