React introduced hooks to bring state management, side effects, and performance optimizations to functional components. Among the most important ones are useEffect, useReducer, and useMemo.
Let’s explore them in detail with icons, syntax, examples, and best practices.
⚡ useEffect – Side Effects
🛠 What does it do?
- Handles side effects (things outside the component’s render).
- Examples include data fetching, subscriptions, timers, and manual DOM updates.
📑 Syntax
useEffect(() => {
// effect logic here
return () => {
// cleanup logic here (optional)
};
}, [dependencies]);
📌 Explanation of Dependencies
- [] empty array: Runs only once (like componentDidMount).
- [state, props]: Runs when listed values change.
- No array: Runs after every render.
🖼 Example
import { useState, useEffect } from "react";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => setCount(c => c + 1), 1000);
return () => clearInterval(timer); // cleanup
}, []);
return <h1>Time: {count}s</h1>;
}
✅ Best Use Cases
- Fetching API data.
- Setting up subscriptions (WebSocket, Firebase, etc.).
- Handling intervals/timers.
- Cleaning up on unmount.
🎛 useReducer – Complex State Management
🛠 What does it do?
- Manages complex or structured state in components.
- Works with a reducer function that defines how state updates happen.
- Inspired by Redux but scoped to the component.
📑 Syntax
const [state, dispatch] = useReducer(reducer, initialState);
📌 Reducer Function
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
🖼 Example
import { useReducer } from "react";
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
<h1>Count: {state.count}</h1>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</>
);
}
✅ Best Use Cases
- Forms with multiple inputs.
- State objects with multiple properties.
- Complex logic where multiple actions update the state differently.
🚀 useMemo – Performance Optimization
🛠 What does it do?
- Memoizes (caches) results of expensive calculations.
- Prevents recalculating unless dependencies change.
- Improves performance by skipping unnecessary work.
📑 Syntax
const memoizedValue = useMemo(
() => computeExpensiveValue(a, b),
[a, b]
);
🖼 Example
import { useState, useMemo } from "react";
function ExpensiveCalculation(num) {
console.log("Calculating...");
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += num;
}
return result;
}
function App() {
const [count, setCount] = useState(0);
const [number, setNumber] = useState(5);
const calculation = useMemo(() => ExpensiveCalculation(number), [number]);
return (
<>
<h1>Calculation: {calculation}</h1>
<button onClick={() => setCount(c => c + 1)}>Re-render {count}</button>
<button onClick={() => setNumber(n => n + 1)}>Increase Number</button>
</>
);
}
✅ Best Use Cases
- Expensive computations (large loops, heavy calculations).
- Derived data that depends on stable inputs.
- Avoid unnecessary recalculations on every render.
Hook |
🔍 Purpose |
⏳ When It Runs |
🎯 Best Use Case |
⚠️ Pitfalls |
⚡ useEffect |
Handle side effects (API calls, timers, subscriptions, DOM updates) |
After render (depends on dependency array) |
Data fetching, event listeners, intervals, and cleanup |
Infinite loops if the dependency array is wrong |
🎛 useReducer |
Manage complex state using reducer & actions |
When dispatch() is called |
Forms, counters, multi-step state logic |
Can be overkill for a simple state |
🚀 useMemo |
Cache expensive calculations to improve performance |
When dependencies change |
Heavy calculations, derived values |
Don’t use for every small calculation (can add overhead) |
🔑 Key Takeaways
- useEffect: Think side effects (runs after render).
- useReducer: Think complex state logic (like Redux inside a component).
- useMemo: Think performance optimization (cache results).
Together, these hooks make React components.
- 👉 Smarter (logic with useReducer)
- 👉 Cleaner (side effects with useEffect)
- 👉 Faster (performance boost with useMemo)