React: Boost performance with effective rendering

Payal Bhalerao
Dec 2, 2019 · 4 min read

We always enjoy fast and responsive user interface, but generally during fast development performance gets neglected. In case of react app development, We generally rely on react’s virtual DOM, that it will perform diffing and re-render only what is changed.

Yes, it does ! But why not to give some hints to skip usless re-rendering, if we can !

Let’s try to understand — What are useless re-renderings ?

In many cases, a React component will re-render when it doesn’t have to. When result of next rendering would be same as previous, then its always better to return and render previous output.

For performance optimization, React offers many higher order components and hooks on which we can rely on.

Two of them are— useMemo() & React.memo()

From useMemo() documentation
Pass a function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies (either a or b) has changed.

React.useMemo(
() => computeExpensiveValue(a, b),
[a, b]
);

This optimization helps to avoid expensive calculations on every render.

From memo() documentation
React.memo is a higher order component. HOC that can optimize rendering of your component given that, it renders the same output with the same properties.

React.memo(function MyComponent(props) {
/* render using props */
});

Since both terminologies have the word memo it might sound a bit confusing for people. but, it's all about React optimization and memoization !

“In computing, memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.”

Difference between useMemo() and React.memo()

useMemo has a different use case than React.memo. It's not about preventing an entire component from re-rendering, it's about simply memoizing some output data. Very unrelated in usage.

The only similarity is that React.memo() and useMemo() both involve "if you see the same inputs as last time, don't do any extra work - return what you had before", but React.memo is for wrapping up entire components, and useMemo() is for whatever you want to return from the callback.

In this post, We will discuss React.memo() in detail, how it improves the performance & when to use it.

What is React.memo()

React.memo is a higher order component.

It’s similar to React.PureComponent but for functional components instead of classes. If your function component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result.

Let’s take one example —

const Counter = ({counter, value}) => {
return (
<div>{counter}: {value}</div>
)
}
export default Counter;
const App = () => {
const [count1, setCount1] = React.useState(0)
const [count2, setCount2] = React.useState(0)

const incrementCounter1 = () => {
setCount1(count1 + 1)
}

return (
<>
<button onClick={incrementCounter1}>
Increase counter 1
</button>
<Counter value={count1} counter={'1'}>Counter1</Counter>
<Counter value={count2} counter={'2'}>Coutner2</Counter>
// counter2 component should not re-render,
// as we are incrementing only counter1
</>
)
}

Every time we clicks on the button, the state of count1 changes causing the app to re-render both counters which is known as useless re-render. However, we expect only the counter1 to be rendered since nothing has changed with the counter2. In reality, both counters get re-rendered.

How can we address this issue?

React.memo is our answer. All we need to do is to wrap our Counter component within React.memo

export default React.memo(Counter);

By default, React.memo will do shallow comparison of props passed to the component. If these props are unchanged, React.memo will reuse the last rendered result, therefore, it prevents the component from being re-rendered.

How to override default comparison

React.memo accepts a second argument, as a comparison function. This makes it comparable to shouldComponentUpdate in class based components.

React.memo(Component, [areEqual(prevProps, nextProps)]);

The comparison function also returns a boolean value. That boolean tells React if it should use the previous result of the component instead of calculating a new one.

  • When true, the function component will not be executed and the previous result will be used instead.
  • When false, the function component will be executed like it normally would.

Watch out! This is the opposite from shouldComponentUpdate!

The comparison function is called with the prevProps and the nextProps. This allows complex logic where the current props are compared to the previous props in order to determine if the output would be different or not, and thus remembered result/memo of the component should be used.

const Counter = ({counter, value}) => {
return (
<div>{counter}: {value}</div>
)
}

const areEqual = (prevProps, nextProps) => {
return prevProps.counter === nextProps.counter &&
prevProps.value === nextProps.value;
}
export default React.memo(Counter, areEqual);

Performance-related changes applied incorrectly can even harm performance. so use React.memo() wisely.

When to use React.memo()

Only for functional component and when there is possibility of re-rendering with same props

As shallow comparison will use object reference, we should use memo() only when — props are not much nested objects, immutability is maintained for objects which we are passing as props

When we are passing functions as a props to component — we should use useCallback, which returns a memoized callback.

useCallback() — This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders

When not to use React.memo()

If the component doesn’t re-render often with the same props.

With useless props comparison.

When props are multilevel nested objects (without maintaining immutability)

Function as a prop, without memoized callback. (As it’s reference get change every-time)

Conclusion

React.memo() is a great tool to memoize functional components. When applied correctly, it prevents component useless rendering when the next props equal to previous.

Profiler is great dev-tool to find the performance issues and to identify whether we need to optimize the component or not.

Payal Bhalerao

Written by

working @joshsoftware, Ruby on Rails & React Js developer. Keen Programmer — Fun Lover — Always Love to help others !

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade