Optimizing React Performance: Preventing Unnecessary Child Component Re-renders

Kaushal Dhakal
4 min readOct 28, 2023

--

React Js Logo

The Problem: To understand the issue better, let’s start with a simple React component structure consisting of a parent component (App) and a child component (Child). The parent manages a state variable, and we want to avoid the child component re-rendering when the parent's state changes.

import React, { useState } from 'react';

function App() {
const [count, setCount] = useState(0);

return (
<>
<h1>Hello</h1>
<Child />
<button onClick={() => setCount(count + 1)}>Click Me!</button>
</>
);
}

const Child = () => {
console.log("==Child rendering====");
return (
<button>
<h3>{'A'}</h3>
</button>
);
}

In this scenario, every time the parent’s state changes, the Child component re-renders unnecessarily, even though its props remains the same.

The Solution: Memoization with React.memo

To solve this problem, we can use React.memo to memoize the Child component. This means the child will only re-render when its props change, effectively preventing unnecessary renders.

import React, { useState } from 'react';

function App() {
console.log("==Parent rendering====");
const [count, setCount] = useState(0);
return (
<>
<h1>Hello</h1>
<Child />
<button onClick={() => setCount(count + 1)}>Click Me!</button>
</>
);
}
const Child = React.memo(() => {
console.log("==Child rendering====");
return (
<button>
<h3>{'A'}</h3>
</button>
);
});

Additional Challenges: What if we need to pass Function/Objects/Maps as props.

import React,{ useState } from 'react'
import './App.css'

function App() {
console.log("==Parent rendering====");
const [count, setCount] = useState(0)
const handleClick = () =>{
//...
}
return (
<>
<h1>Hello</h1>
<Child handleClick={handleClick} />
<button onClick={() =>setCount(count+1)}>Click Me!</button>
</>
)
}
const Child = React.memo(({handleClick}) => {
console.log("==Child rendering====");
return (
<button>
<h3>{'A'}</h3>
</button>
);
})

export default App

Here, we are passing a new inline function (handleClick) as a prop to the Child component every time the parent component re-renders. Even though the Child component is memoized, the new handleClick function is a different reference on every render. This causes the Child component to re-render, as it sees a new prop reference, even though the function itself has not changed.

Using useCallback:

import React,{ useCallback, useState } from 'react'
import './App.css'

function App() {
console.log("==Parent rendering====");
const [count, setCount] = useState(0)
const handleClick = useCallback(() =>{
//...
},[])
return (
<>
<h1>Hello</h1>
<Child handleClick={handleClick} />
<button onClick={() =>setCount(count+1)}>Click Me!</button>
</>
)
}

const Child = React.memo(({handleClick}) => {
console.log("==Child rendering====");
return (
<button>
<h3>{'A'}</h3>
</button>
);
})

export default App

useCallback is a hook in React that memoizes functions. It's useful when you want to prevent the recreation of a function on every render, especially when that function is used as a prop for child components.

Here, handleClick is memoized using useCallback. The second argument ([]) is an empty dependency array, meaning the function is memoized once during component initialization and will always have the same reference across re-renders. This prevents unnecessary re-creation of the handleClick function on each render, optimizing performance.

Using useMemo:

import React, { useState, useMemo } from 'react';

function App() {
console.log("==Parent rendering====");
const [count, setCount] = useState(0);

// Compute a value memoized with useMemo
const computedValue = useMemo(() => {
//Some Expensive computation
return count * 2;
}, [count]);

return (
<>
<h1>Hello</h1>
<Child value={computedValue} />
<button onClick={() => setCount(count + 1)}>Click Me!</button>
</>
);
}

const Child = React.memo(({value}) => {
console.log("==Child rendering====");
return (
<button>
<h3>{'A'}</h3>
</button>
);
})

export default App;

useMemo is another hook in React, but it's used to memoize values, not functions. It's handy when you want to avoid recomputing a value on every render, especially when that computation is expensive.

In this example, the computedValue is calculated using useMemo. It depends on the count state variable, so it will be recomputed only when count changes. The expensive computation is performed only when necessary, helping to optimize performance by avoiding needless recalculations.

In summary, useCallback is used to memoize functions, preventing unnecessary function recreations, while useMemo is used to memoize values, optimizing performance by avoiding unnecessary recomputation. Both hooks are valuable tools for improving React application performance.

Note(🔥🔥🔥🔥🔥🔥🔥🔥🔥):

import React,{ useCallback, useState } from 'react'
import './App.css'
function App() {
console.log("==Parent rendering====");
const [count, setCount] = useState(0)
const handleClick = useCallback(() =>{
//...
},[])
return (
<>
<h1>Hello</h1>
<Child handleClick={() => handleClick()} />
<button onClick={() =>setCount(count+1)}>Click Me!</button>
</>
)
}
const Child = React.memo(() => {
console.log("==Child rendering====");
return (
<button>
<h3>{'A'}</h3>
</button>
);
})

export default App

In the provided code example, passing a function to a child component with an inline arrow function like this: <Child handleClick={() => handleClick} /> creates a new function reference on each parent component render, even though it essentially points to the same handleClick function. This results in unnecessary re-renders of the child component, even when the underlying handleClick function hasn't changed. To avoid these unnecessary re-renders and ensure optimal performance when using React.memo, it's best to directly pass the handleClick function as a prop, like this: <Child handleClick={handleClick} />. This way, the child component accurately identifies that its props, including the handleClick function, haven't changed, and it won't re-render needlessly. It's an important optimization to consider when working with React components to maintain a responsive and efficient application.

--

--

Kaushal Dhakal

Software Engineer . I write blog posts about web development, JavaScript, Java ,React, Node and Problem Solving.