By now, you learned how to build pretty complex applications. But, as the complexity of your application grows, you need to be aware of how to optimise it when it starts to become sluggish. Often, the proble is the number of unnecessary re-renders. Let’s first create a scenario where a child component re-renders every time the parent component re-renders, even if its input (props) remains unchanged.
import React from 'react';
const NonPureComponent = ({ message }) => {
console.log('NonPureComponent rendered');
return <div>{message}</div>;
};
const ParentComponent = () => {
const [count, setCount] = React.useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment Count: {count}</button>
<NonPureComponent message="Hello from Non-Pure Component!" />
</div>
);
};
export default ParentComponent;
ParentComponent
holds a count
state and provides a button to increment this count. Each time the button is clicked, setCount
triggers a re-render of the ParentComponent
.NonPureComponent
is a simple functional component that displays a message
prop.message
prop remains constant, the child component is re-rendered every time the parent updates its state.When you interact with the button, you will notice that console.log('NonPureComponent rendered')
runs every time, even though the message
prop hasn’t changed. This happens because React functional components are not "pure" by default—they re-render when their parent re-renders.
While a simple example like this doesn’t cause noticeable issues, in larger applications, unnecessary renders can lead to:
useEffect
or other hooks tied to rendering, these might be triggered even when they aren’t needed, leading to bugs or inefficient behavior.React.memo
: Making the Component PureWe can solve this problem by wrapping the child component with React.memo
, a higher-order component (HOC) that optimizes functional components by preventing re-renders when props don’t change.
React.memo
import React from 'react';
const PureComponent = React.memo(({ message }) => {
console.log('PureComponent rendered');
return <div>{message}</div>;
});
const ParentComponent = () => {
const [count, setCount] = React.useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment Count: {count}</button>
<PureComponent message="Hello from Pure Component!" />
</div>
);
};
export default ParentComponent;
React.memo
WorksReact.memo
, React compares the new props with the previous props.count
in ParentComponent
, PureComponent
no longer re-renders because its message
prop doesn’t change.You can observe this in the console—console.log('PureComponent rendered')
only runs on the initial render and does not trigger again, even when the parent updates.
React.memo
is ImportantBy reducing unnecessary renders, React.memo
helps optimize your application’s performance. In real-world applications with deep component trees or computationally expensive operations, avoiding redundant renders can make a significant difference.
Using React.memo
enforces a more predictable rendering behavior. Developers can reason more easily about when a component will re-render, based on changes to its props.
For components that perform heavy rendering logic (e.g., complex calculations, rendering lists with thousands of items), React.memo
ensures that they only render when their inputs (props) change, reducing the CPU workload.
React.memo
While React.memo
is a powerful tool, it’s essential to use it judiciously:
React.memo
can add unnecessary complexity and marginal performance gains in simple components.React.memo is a simple yet powerful optimization tool for functional components. It allows you to transform impure components, which re-render unnecessarily, into pure components that only re-render when required. This not only improves your app's performance but also makes its behavior more predictable and resource-efficient. By understanding and strategically using React.memo
, you can build React applications that scale effectively without compromising on performance or maintainability.
By now, you learned how to build pretty complex applications. But, as the complexity of your application grows, you need to be aware of how to optimise it when it starts to become sluggish.
Often, the problem is the number of unnecessary re-renders. Let’s first create a scenario where a child component re-renders every time the parent component re-renders, even if its input (props) remains unchanged.
How this works? The ParentComponent holds a count state and provides a button to increment this count. Each time the button is clicked, setCount triggers a re-render of the ParentComponent
The NonPureComponent is a simple functional component that displays a message prop. Even though the message prop remains constant, the child component is re-rendered every time the parent updates its state.
When you interact with the button, you will notice that console-log statement runs every time, even though the message prop hasn’t changed. This happens because React functional components are not "pure" by default—they re-render when their parent re-renders. While a simple example like this doesn’t cause noticeable issues, in larger applications, unnecessary renders can lead to Performance Bottlenecks and Unintended Side Effects.
We can solve this problem by wrapping the child component with React-memo, a higher-order component that optimises functional components by preventing re-renders when props don’t change.
How does it work? By wrapping the functional component with React-memo, React compares the new props with the previous props. If the props haven’t changed, React skips re-rendering the component. In our example, when you increment the count in ParentComponent, PureComponent no longer re-renders because its message prop doesn’t change.
But, if we include the value of the count as a prop of our Pure component ...
... as expected, clicking on the button will trigger a re-render for each click.