Preventing Unnecessary Rerendering of Child Components in React Using useCallback and React.memo

Akash Shukla
4 min readMar 25, 2023

--

React is a popular JavaScript library used for building web applications. One of the main features of React is its ability to efficiently update and render the components of a web application. However, as an application grows in complexity, it can become increasingly difficult to manage the rendering of child components within a parent component.

To address this issue, React provides two hooks, useCallback and React.memo, which can be used together to prevent the unnecessary re-rendering of child components within a parent component.

I have created a simple react program to showcase an example with code and highlighting. please take a look https://codesandbox.io/s/elated-resonance-i86qql?file=/src/App.js

Before going further into it, we should know what makes a component re render. let’s assume them as rules for sometime.

  1. state changes
  2. props changes
  3. Re render of Parent Component.

Let’s consider an example, we have a parent component App and a child component Card, we are passing props in child component, and we don’t want our child components to re-render every time something changes in the parent component which is not directly impacting child component.

Problem Statement : Every time some state changes in the parent component, it makes the child component to re render. But many times Child components do not require re renders as those states are not used by all child components.

for example, lets look at this code, in App component we are using Card component and passing props: count and increment.

now every time we click on “Update Parent to Cause Re render” button. It will re-render Card component. why? because of state change in App component (rule 1), makes it re render and re-render of parent component makes Card re render (rule 3).

Sample UI for problem statement
export default function App() {
const [random, setRandom] = useState(10);
const [count, setCount] = useState(0);

const increment = () => (setCount(count + 1));

const newRandom = () => setRandom(Math.floor(Math.random() * 100));

return (
<div id="parent">
<Card count={count} updateCount={increment} />
{random}
<button onClick={newRandom}>
Update Parent to Cause Rerender
</button>
</div>
);
}

To avoid re rendering of component if props are not changing , we can use “React.memo” and thus our card component now looks like this

// Card.jsx
function Card({ count, updateCount }) {
return (
<div id="card" className="card-default">
This is Card Component
current Count : {count}
<button onClick={updateCount}>
Update Count to Cause Rerender Child
</button>
</div>
);
}
export default React.memo(Card)

okay, now we have used memo, and wait, what? still it is making card component re render when we click on “Update Parent to Cause Re render” . why? this is because when our parent component re renders everything gets recreated, including the function reference to “increment()”. Since, the reference of function is now changed. for child component it is an update, As a result, the props change and the card component gets re-rendered, in accordance with rule 2.

Now we are set, if somehow, we could tell react, that “hey react you don’t need to re initialize the function unless we ask you for it”. to solve this problem specifically React gives us useCallback hook.

useCallback(function, [dep1, dep2])

With useCallback we can define a function that has referential equality between renders. it will only recompute the function when its dependency changes and return a memoized callback.

After wrapping our increment function with useCallback, we’ve noticed that the Card component no longer re-renders upon state updates in the parent component for states that aren’t being used by the Card component. This is because the useCallback hook helps to optimize the performance of our code by memoizing the function reference and only updating it when necessary. So, our final code now looks like this.

//App.jsx
export default function App() {
const [random, setRandom] = useState(10);
const [count, setCount] = useState(0);

const increment = () => (setCount(count + 1));
//
const memoizedUpdateCount = useCallback(increment, [count]);
return (
<div id="parent">
<Card count={count} updateCount={memoizedUpdateCount} />
{random}
<button
onClick={() => {
setRandom(Math.floor(Math.random() * 100));
}}
>
Update Parent to Cause Rerender
</button>
</div>
);
}
// Card.jsx
function Card({ count, updateCount }) {
return (
<div id="card" className="card-default">
This is Card Component
current Count : {count}
<button onClick={updateCount}>
Update Count to Cause Rerender Child
</button>
</div>
);
}
export default React.memo(Card)

I have created a simple react program to showcase above example with animation. please take a look https://codesandbox.io/s/elated-resonance-i86qql?file=/src/App.js

--

--

Akash Shukla

Senior Software Engineer - Fullstack ( Javascript, Python, Go, React, Flask, FastAPI, Gin, Postgres, Typescript)