why we need useCallback in React
In React, useCallback
is a hook that returns a memoized version of a callback function. It helps to optimize performance by ensuring that the same function instance is used between renders unless one of its dependencies changes. This can prevent unnecessary re-renders of child components and avoid unnecessary re-creations of functions, which can be particularly beneficial in performance-sensitive applications.
Reasons to Use useCallback
-
Prevent Unnecessary Re-renders:
- When a parent component re-renders, it creates new instances of all its functions. If these functions are passed as props to child components, the child components will re-render even if their props haven't changed.
useCallback
ensures that the function reference remains the same between renders unless dependencies change, preventing unnecessary re-renders of child components.
-
Optimize Performance:
- Reducing the number of function re-creations can save memory and improve performance, especially in components that rely on these functions for expensive operations or complex logic.
-
Stabilize Function References:
- In contexts where stable function references are required, such as dependencies for other hooks (e.g.,
useEffect
),useCallback
ensures that the dependencies do not change unless necessary.
- In contexts where stable function references are required, such as dependencies for other hooks (e.g.,
Example Without useCallback
Consider a scenario where a parent component passes a callback to a child component:
import React, { useState } from 'react';
const ChildComponent = React.memo(({ onClick }) => {
console.log('ChildComponent rendered');
return <button onClick={onClick}>Click me</button>;
});
const ParentComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<ChildComponent onClick={handleClick} />
</div>
);
};
export default ParentComponent;
In this example, every time ParentComponent
re-renders, a new handleClick
function is created, causing ChildComponent
to re-render even though its props haven't changed.
Example With useCallback
Now, let's optimize the example using useCallback
:
import React, { useState, useCallback } from 'react';
const ChildComponent = React.memo(({ onClick }) => {
console.log('ChildComponent rendered');
return <button onClick={onClick}>Click me</button>;
});
const ParentComponent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<ChildComponent onClick={handleClick} />
</div>
);
};
export default ParentComponent;
Explanation
-
Memoized Function:
handleClick
is now memoized usinguseCallback
, meaning that it will only be recreated if thecount
changes.- This ensures that the
onClick
prop passed toChildComponent
remains the same unlesscount
changes, preventing unnecessary re-renders ofChildComponent
.
-
Dependencies:
- The dependencies array
[count]
ensures thathandleClick
is only recreated whencount
changes, keeping the function reference stable across renders.
- The dependencies array
When to Use useCallback
- Passing Callbacks to Child Components: When you pass a callback function to a child component that uses
React.memo
to prevent unnecessary re-renders. - Dependencies for Other Hooks: When you need a stable function reference as a dependency for other hooks like
useEffect
oruseMemo
. - Performance Optimization: In performance-sensitive components where function re-creations could lead to significant overhead.