React provides several Hooks to manage state, optimize performance, and organize component logic.
1. useCallback Hook
What is useCallback?
useCallback is a React Hook that caches a function definition between renders until its dependencies change.
It is useful when we want to avoid creating a new function reference on every render, especially when passing callbacks to memoized child components.
Syntax
const cachedFunction = useCallback(() => {
// function logic
}, [dependencies]);
- Callback function: The function we want to cache.
- Dependency array: Values that the function depends on.
- When a dependency changes, React returns a new function definition.
Example
import { useState, useCallback } from "react";
function App() {
const [count, setCount] = useState(0);
const showMessage = useCallback(() => {
console.log("Button clicked");
}, []);
return (
<div>
<h2>Count: {count}</h2>
<button onClick={() => setCount(count + 1)}>
Increase
</button>
<button onClick={showMessage}>
Show Message
</button>
</div>
);
}
export default App;
How does it work?
-
useCallbackcaches theshowMessagefunction. - The empty dependency array means the function reference remains cached across renders, provided the component remains mounted.
- Clicking the Increase button changes the state and re-renders the component.
- React can reuse the cached function reference.
Important: useCallback does not prevent a component from re-rendering by itself. It is most useful when function identity matters, such as when passing a callback to a child wrapped in React.memo.
Why do we use useCallback?
- To preserve function references between renders.
- To help optimize memoized child components.
- To avoid unnecessary effect re-runs caused by changing function dependencies.
2. useMemo Hook
What is useMemo?
useMemo is a React Hook that caches the result of a calculation between renders until its dependencies change.
It is useful when a calculation is expensive and does not need to be repeated when its inputs remain the same.
Syntax
const cachedValue = useMemo(() => {
return calculateValue();
}, [dependencies]);
- Callback function: Contains the calculation.
- Dependency array: Contains the values used by the calculation.
- Return value: The calculated result, which React can reuse while the dependencies remain unchanged.
Example
import { useState, useMemo } from "react";
function App() {
const [number, setNumber] = useState(5);
const [count, setCount] = useState(0);
const square = useMemo(() => {
console.log("Calculating square");
return number * number;
}, [number]);
return (
<div>
<h2>Number: {number}</h2>
<h2>Square: {square}</h2>
<h2>Count: {count}</h2>
<button onClick={() => setNumber(number + 1)}>
Change Number
</button>
<button onClick={() => setCount(count + 1)}>
Increase Count
</button>
</div>
);
}
export default App;
How does it work?
Initially, number is 5, so the square is 25.
When the Increase Count button is clicked, count changes, but number remains the same. React can reuse the cached square value instead of recalculating it.
When the number changes, React recalculates the square.
Why do we use useMemo?
- To cache expensive calculation results.
- To avoid unnecessary recalculations.
- To help optimize rendering when calculations are costly.
Important: Do not use useMemo for every calculation. Simple calculations usually do not need it. React treats memoization as an optimization, not a guarantee.
3. useReducer Hook
What is useReducer?
useReducer is a React Hook used to manage state through a reducer function and actions.
It is useful when state logic becomes complex or when several actions need to update the same state.
Syntax
const [state, dispatch] = useReducer(reducer, initialState);
useReducer returns an array containing two values:
-
state— the current state. -
dispatch— a function used to send an action to the reducer.
Important Terms
Reducer function: Receives the current state and an action, then returns the next state.
Action: An object that describes what should happen.
Dispatch: Sends an action to the reducer.
Initial state: The starting state value.
Example: Counter
import { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "increase":
return { count: state.count + 1 };
case "decrease":
return { count: state.count - 1 };
case "reset":
return { count: 0 };
default:
return state;
}
}
function App() {
const initialState = { count: 0 };
const [state, dispatch] = useReducer(
reducer,
initialState
);
return (
<div>
<h2>Count: {state.count}</h2>
<button onClick={() => dispatch({ type: "increase" })}>
Increase
</button>
<button onClick={() => dispatch({ type: "decrease" })}>
Decrease
</button>
<button onClick={() => dispatch({ type: "reset" })}>
Reset
</button>
</div>
);
}
export default App;
How does it work?
- The initial state is
{ count: 0 }. - Clicking Increase calls
dispatch({ type: "increase" }). - The reducer receives the current state and the action.
- The reducer returns a new state object.
- React updates the state and re-renders the component.
Why do we use useReducer?
- To organize complex state logic.
- To handle multiple types of state updates.
- To keep state transitions in one reducer function.
- To manage related state values together.
For example, useReducer can be useful in a shopping cart, form management, or a task management application.
Difference Between useCallback, useMemo, and useReducer
| Hook | Main purpose | Returns |
|---|---|---|
useCallback |
Caches a function definition | Cached function |
useMemo |
Caches a calculation result | Cached value |
useReducer |
Manages state using actions and a reducer | Array containing state and dispatch |
Conclusion
useCallback and useMemo help optimize performance when caching is useful. useReducer helps manage state updates through a structured reducer function. Understanding these Hooks makes it easier to build React applications that are organized, maintainable, and efficient.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.