How to make your functional React components perform better (with useCallback and memo)
Tags: javascript, react, webdev, tutorial
Published: 2019-11-02 08:38:17

These days I am getting into React after 4 years of AngularJs and Angular 2+. I really like the unopinionated nature of this library and the flexibility to decide on patterns and implementation details. 

With lots of choices you make - comes a big number of mistakes that can follow up these choices and more work we need to do as developers to ensure we are making our best to optimize our web applications and decide on the right patterns.

In this post, I will cover some optimization techniques I learned that may be useful for you. 

This is my first React related post and I hope you'll like it.

<b>React hooks</b>

React hooks were a new addition in React 16.8. They give you an ability to use state without using javascript classes. These are very powerful and easy to use tools. I will not cover hooks basics here, you can learn them by yourself <a href='https://reactjs.org/docs/hooks-reference.html'>using official API reference</a>, but I will use them in the demo application.

<b>Demo application</b>

For this post, I created a simple React application that we will change in a few ways to improve its performance. It's small and simple, but it's enough to describe the issues that we are going to solve in the next 5 minutes.

It's a number selection component and you can see it here and explore the code:
{% codepen https://codepen.io/erictheodore/pen/rNNYBGa %}

We have 2 components.
<b>NumPad</b> component represents a button having a number. As a props, it receives a <b>value</b> - a number to display, <b>handleClick</b> a callback function to handle clicks and <b>isSelected</b> - a boolean indicating if the button should get selected class or not. In case button has a positive <b>isSelected</b> - it will get a green color to indicate the selection.

The second and the bigger component is <b>NumberSelection</b>. This component has stateful logic and handles the <b>selectedValue</b> variable using <b>useState</b> hook. <b>NumberSelection</b> renders 10 buttons in the loop and one of them that is equal to <b>selectedValue</b>  is getting <b>isSelected</b> class. It also represents the selected value inside the header element. Initial <b>selectedValue</b> equals 5.

Easy until now?

Now let's dive into the problems this code has.

<b>Functional components</b>

In 2019 functional components are considered a <a href='https://programmingwithmosh.com/react/react-functional-components/'>better practice</a> than class components. With the help of hooks, they now allow creating stateful logic. 

There is 1 important thing that we need to remember about functional components - <b>they are functions</b> that run on each render, meaning that every single thing inside them is invoked and every variable or function is declared again.

Inside <b>NumberSelection</b> we have a function called <b> changeSelection</b>. Every time the state of the component is changed - component is rendered and the function is declared again and again. To show this in a more visual way, I changed our original code and added a set called <b>functionsSet</b>. I will add our <b> changeSelection</b> to that set every time the component is rendered.

{% codepen https://codepen.io/erictheodore/pen/PooENzj %}
As you see, every time we change the selection - a new function reference is created and added to the set. This is definitely not so good for our memory and in some cases can cause memory leaks. But even a bigger problem - this function is passed to child components using props. This means that they will be rerendered too because of the change in the props passed inside.

What can help us here as a hook called <a href='https://reactjs.org/docs/hooks-reference.html#usecallback'><b>useCallback</b></a>. It will create a <a href='https://en.wikipedia.org/wiki/Memoization'>memoized</a> version of the function that will be reused when the component will be rerendered.

Let's rewrite <b>changeSelection</b> declaration using <b>useCallback</b> hook:
See updated codepen example:
{% codepen https://codepen.io/erictheodore/pen/oNNpxqz %}
We are not redeclaring them anymore. Great success!

One more thing to remember here - in many cases there is no need to keep stateless functions inside functional components. If your function is completely static - consider placing it outside, move it to utils library or just before the component is declared. This will keep it from being redeclared even without the usage of <b>useCallback</b>. 

<b>What are we really rendering?</b>

There is a big number of <a href='https://medium.com/@gethylgeorge/how-virtual-dom-and-diffing-works-in-react-6fc805f9f84e'>great articles</a> explaining in-depth how rendering works overall and in React applications. I will not dive into it here as it will take too much time. I assume that you know what React Virtual Dom is and how it works.

How our application is rendered? What happens when we change the selection and the state of <b>NumberSelection</b> is updated?

1. The whole application is rendered for the first time.
2. We click the button represented by <b>NumPad</b> component.
3. This calls the callback function received from <b>NumberSelection</b> component.
4. State of <b>NumberSelection</b> component is changed as a result of the callback.
5. Once the state is changed - the component that was using the state and the tree of the child components are rebuilt in virtual DOM.
6. Once the virtual DOM is ready - the diffing algorithm decides which parts of real DOM should be updated. Assuming we changed the selection - it updates 2 <b>NumPad</b> components - one is deselected and one is now selected.

That is nice to understand. 
I added a new log written when <b>NumPad</b> is rendered.
{% codepen https://codepen.io/erictheodore/pen/abbEZQV %}

As you see - <b>NumPad</b> is rerendered on each state change of parent component - meaning that on every click we are rendering all 10 buttons again. This is a rendering done for the virtual DOM - the component is not really updated in the real DOM, but we still invoke the whole render process. This is a lot of code running. Do we really need this? What if we have 100 buttons, 1000 buttons?

On every selection change, we have only 2 <b>NumPad</b> components that are actually changed - the one that was selected - it will get the selection now, and the old one that is now deselected. We don't really need to render all 10 <b>NumPads</b> again.

How can we know if a component should be rendered or not? Assuming that components are pure functions - we can just look at the props passed into. If they are changed - that's the sign we need to render the component. If not - we don't need to render them.

This is a place we should consider using <a href='https://reactjs.org/docs/react-api.html#reactmemo'><b>React.memo</b></a>. It does exactly what we need. 

React API says:
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. This means that React will skip rendering the component, and reuse the last rendered result.

Sounds like our case! Let's wrap our <b>NumPad</b> in React.memo:
{% codepen https://codepen.io/erictheodore/pen/abbEBme %}

Now we see that only the 2 relevant components are rendered. Great success again!

One thing that worth mentioning here - without using <b>useCallback</b> hook from the previous example - this optimization was not working. Because without <b>useCallback</b> hook new function is generated every time and is passed to all components, meaning the React.memo will detect change prop value and render the component.

If <b>React.memo</b> is so useful for us, why can't React just wrap all components by default?

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/as1vasj26850q3rdoj1j.png)

Remember that using <b>memo</b> and <b>useCallback</b> should not be done by default by you. Check the exact reference and think of each case separately as a misunderstanding of the way they should be used can cause side effects and bugs in your code.

Hope you had fun!
Will be happy to get your feedback on my post.

Check out my <a href='https://dev.to/shadowwarior5/10-superpowers-that-html5-gives-you-and-you-are-not-using-4ph1'>previous post</a> talking about interesting features in HTML5.

<a href='https://twitter.com/meiklyar'>Follow me on Twitter</a> to get my latest updates!

See you