Performance Optimization with React Hooks and Memo

Improve application performance with functional components

Clarian North
Jan 13 · 7 min read
Illustration by DBSand on Wikimedia Commons

Building a useful application is one thing, having the application load smoothly and quickly is something altogether else — and, arguably, of equal importance.

There are many way to go about optimizing an application in React to improve its performance.

Before we begin examining how to do this by building a simple app and implementing the memoization technique included in React Memo, let’s review the process of optimization.

Optimization is a process that’s most effective when measured against an initial model. If we are overly preemptive with optimization by implementing tools such as React Memo without having an initial measurement to compare to, we no longer provide ourselves the needed context to properly evaluate how, when, and where we’re making our improvements — thus failing to deliver proper optimization.

Patience and letting the process flow is key to successful optimizations and smooth running applications, young Padawan.

Therefore, it’s important for us to compare the differences of performance measurements.

In the following examples, we’ll look at a simple application leveraging React Hooks and measure the difference of performance with and without React Memo.

Feel free to code along and even follow the video demonstration at the bottom or to simply observe the differences in the documentation below. But first, let’s further define React Memo.


React Memo vs. PureComponent

React Memo is a higher-order component that wraps around functional components and improves their performance by memoizing them.

In this sense, it’s the solution for functional components that React.PureComponent provides for class-based components.

Comparatively, each feature would be superficially introduced to their components, as seen below:

// PureComponent Class Based:
class Count extends React.PureComponent { }

Versus:

// React.Memo Functional Component:
const App = ({}) => {}
export default React.memo(App)

How React Memo Works

React Memo improves performance by only rendering its wrapped component if the props have changed. However, it’s important to note that by default, React Memo will only shallowly compare complex objects in the props object.

We can further compare how this works by looking at an example use of the life-cycle method shouldComponentUpdate(nextProps, nextState).

We can utilize shouldComponentUpdate to condition whether or not a component should update on the render based on checking if the nextProps being updated are equal to the current props.

Although this implementation in effect works similarly to React Memo, the official React docs suggest not to use it to prevent rerendering — as it may lead to bugs. Instead, they suggest using its features designed to be more comprehensive, ensuring there will be less potentially necessary steps skipped.


React Application Example

Let’s now examine a basic application and measure its performance pre- and postmemoization with React.Memo using the React Dev Tools.

Say we have an application with two basic components: a button that increments consecutively on each click by one and a weather component that displays a city and the weather in that city.

First, examine a JavaScript file containing our Weather child component with the following code:

Our simple functional component takes the destructured weather property as its props and returns two p tags, displaying further dot notation accessing city and temperature.

We include a console.log(‘Render’) to demonstrate in our upcoming test how many times our application is rendering our weather application.

Simple enough. All right, now observe another JavaScript file that contains our button application and holds our parent state, which passes props into our child Weather component.We will implement React Hooks to store our counter and set the increment.

Now when we load our development server and compile our application, we display a button and a 0 — along with the city of Miami and its temperature at 80F.

We also note in the Chrome DevTools Console that we receive the initial console.log of the render from our Weather component.

The problem here becomes evident whenever we click on the Increment button.

Clicking on the Increment button seven times returns the following results:


Implementing React.memo

Although our button is working and our state is being updated and displayed accordingly, our console is now showing that our Weather component has been rendered seven times.

These are useless renders happening every time we click on the button because the properties of our Weather component doesn’t need to change every time we change the state of the button, and this is costing us extra unnecessary computation.

So how can we solve this problem? Well you guessed it … React.memo to the rescue!

Back in our weather application, we can wrap React.memo around our export of Weather, like such:

export default React.memo(Weather)

And it’s as simple as that. Now when we refresh our application and click on the Increment button seven times again. we’ll notice the following important change.

Our Weather component is only rendered once although our button has been rendered seven times. We have effectively enhanced the performance of our application by successfully implementing memoization.

Let’s further demonstrate the measured differences of our optimization with React Developer Tools.


React Developer Tools Profiler

If you don’t already have React Developer Tools installed, you can do so by going to the link provided here.

Click on the blue button on the top-right corner to add the extension to Chrome.

Once the extension is added, you’ll now have added React components, including Profiler, to your console-developer toolbelt.

The Components option will open up a tab where we can examine the individual components in our React Application being rendered on the browser, much like the Elements tab examines the elements.

Our Weather component now displays to us the props of weather in our application. That’s great. However, for the purposes of this demonstration, let’s move onto the Profiler to measure the performance of our render times.

Let’s refresh the application in the browser. Then, in our Profiler tab, if we click on the record circle, we’re now recording the render times of our components. Let’s click on the increment button six times and then hit the record button again to record our measurements.

We now see our Weather component, wrapped in (Memo), is displayed along with the status “Did not render during the profile session.”

Now, let’s repeat the same process again — only this time, we’ll remove the React.memo higher order component from our child Weather component.

We’ll now see six new renders of our Weather component per each application render, averaging each at about one extra millisecond per render.

By measuring our render differences with the React Developer Tools GUI, we’re able to more deeply see the varying results. We withhold useless renders, leveraging React.memo into our application.


Conclusion

By first building our application without preemptively applying optimizations and instead having an initial comparison value, we’re able to measure our performance with our own test implementations — as well as bring in helpful extensions such as the React Developer Tools.

With these results, we can then review the performance of our application and devise methods and features for enhancement and optimization. Measuring against previous results will lead us to dive deeper into our applications and find the best solutions we need for our optimization.

For additional resources, please check out the Performance Optimization section on React Dev 2020, which inspired this documentation I’ll link below.

For more information on memoization, you can check out a previous piece and example I wrote on reviewing memoization in Javascript below as well.

Finally, I’ll also link to a video tutorial as well as source code.

And that’s it. Thanks for checking this out, and I hope you found some of this helpful!

Resources

Better Programming

Advice for programmers.

Clarian North

Written by

Clarian is a mastering and product engineer with over 10 years of professional experience as well as a passionate full stack web developer and writer.

Better Programming

Advice for programmers.

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