DEV Community

Marco Kühbauch
Marco Kühbauch

Posted on • Edited on • Originally published at marcokuehbauch.com

🚀 React performance quick wins 🚀 - 7 easy ways to improve your React performance - part 1

At the beginning of the year I wrote a blog post about React performance improvements. It‘s one of my favourite blog posts I ever wrote. It’s a great example of documenting everything while you learn it.

It’s a very in depth analysis. It shows you how to test and measure the performance of your React application and a lot of ways how to improve it.

In the contrary this blog post has the goal to give you a lot of quick wins for your performance. Things you can change and adjust, without too much effort.

So you can see it as an addition to my original react performance blog post. The first one builds the foundation. This one adds the cherry on top.

This post is part 1 of a series.

Part 2: Coming Soon

What you can learn from this blog post:



Always use a key when mapping (and not an index)

When mapping over an array and rendering a list of items, make sure you always use keys for every item.

Keys help React identify which items have changed, are added, or are removed. Keys should be strings that identify a list item, such as an ID.

Here is a quick example of the usage of keys. Check the return method of this component. There we use the id of every animal as a key.

import * as React from 'react';
import { AnimalCard } from './animal-card';

const animals = [
  {
    id: 1,
    name: 'dog',
  },
  {
    id: 2,
    name: 'cat',
  },
  {
    id: 3,
    name: 'unicorn',
  },
];

const Animals = () => {
  return animals.map(animal => {
    return <AnimalCard key={animal.id}>{animal.name}</AnimalCard>;
  });
};

export default Animals;
Enter fullscreen mode Exit fullscreen mode

If you're not using keys, React has to a hard time to figure out which items to update. This can lead to updating every item although only one changed.

This can be a massive performance impact! 😫

If you don't have access to unique identifiers, indexes can be used as a last resort. It is not recommended to use them as keys but it's better than not using any keys at all.

If the order of items changes, the index changes as well so React has to update every item anyway.

You can learn more about why keys are important in this article about reconciliation.

So that's it for quickwin number one: always use a key when mapping!

Use React.Fragments instead of divs as JSX parent element

Next up: quickwin number two.

When you return multiple elements from a component, JSX needs a parent element to wrap them. A quick way to do this, is to put a <div> around them.

I admit it, I've done this too, way too many times.

import * as React from 'react';

const MyComponent = () => {
  return (
    <div>
      <span>Hi!</span>
      <span>Hope you can learn a lot from my blog posts</span>
    </div>
  );
};

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

But the next time you need a JSX wrapping element, think again! You can use a React.Fragment for that!

import * as React from 'react';

const MyComponent = () => {
  return (
    <React.Fragment>
      <span>Hi!</span>
      <span>Hope you can learn a lot from my blog posts</span>
    </React.Fragment>
  );
};

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

And the best thing is, there is even a short syntax for this.

import * as React from 'react';

const MyComponent = () => {
  return (
    <>
      <span>Hi!</span>
      <span>Hope you can learn a lot from my blog posts</span>
    </>
  );
};

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

But wait, why does this boost my performance? Good question! Here's the thing. The more DOM elements your React application has, the longer the render process takes.

Google's performance tool lighthouse recommends not more than 1500 DOM nodes in total.

So by removing every unnecessary div, like the one we've used way too often as a JSX parent element wrapper, we already get closer to that goal. And we can boost our performance!

You can read more about why you should avoid too many DOM nodes in this article: https://web.dev/dom-size/

One useState hook instead of multiple for connected data

Another performance quickwin you can achieve easy, is using one useState hook instead of multiple ones for connected data.

Imagine a developer built a form with several input elements. Therefore they added four individual useState hooks.

That could look something like this:

import * as React from 'react';

const MyBigForm = () => {
  const [username, setUsername] = React.useState('');
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');
  const [nickname, setNickName] = React.useState('');

  const handleUserNameChange = event => {
    setUsername(event.target.value);
  };

  const handleEmailChange = event => {
    setEmail(event.target.value);
  };

  const handlePasswordChange = event => {
    setPassword(event.target.value);
  };

  const handleNicknameChange = event => {
    setNickName(event.target.value);
  };

  return (
    <form>
      <label>Username:</label>
      <input value={username} name="username" onChange={handleUserNameChange} />

      <label>Email:</label>
      <input value={email} name="email" onChange={handleEmailChange} />

      <label>Password:</label>
      <input value={password} name="password" onChange={handlePasswordChange} />

      <label>Nickname:</label>
      <input value={nickname} name="nickname" onChange={handleNicknameChange} />
    </form>
  );
};
Enter fullscreen mode Exit fullscreen mode

There is a lot of useState and onChange going on here, right? Phew. We can optimize for the better here!

By removing the individual useState hooks and implementing a combined one, we're making sure to reduce the size of the component and therefore improve the performance.

And we can also get rid of all the handleChange functions, which all seem to do the same.

import * as React from 'react';

const MyBigForm = () => {
  const [formdata, setFormdata] = React.useState({
    username: '',
    email: '',
    password: '',
    nickname: '',
  });

  const handleOnChange = event => {
    setFormdata({
      ...formData,
      [event.target.name]: event.target.value,
    });
  };

  return (
    <form>
      <label>Username:</label>
      <input value={username} name="username" onChange={handleOnChange} />

      <label>Email:</label>
      <input value={email} name="email" onChange={handleOnChange} />

      <label>Password:</label>
      <input value={password} name="password" onChange={handleOnChange} />

      <label>Nickname:</label>
      <input value={nickname} name="nickname" onChange={handleOnChange} />
    </form>
  );
};
Enter fullscreen mode Exit fullscreen mode

Wow a lot less code and a lot more readable, don't you think? It's always a good idea to keep your state in an object, if you're dealing with connected data.

Don't get me wrong: using and calling multiple useState hooks is not a bad thing! React has no problem with that.

But in this example it's also about removing duplicate code and rethinking your components.

And if JavaScript has to allocate the memory for only one function instead of four, that's also a performance quickwin! And that's what we're here for, right?

That's it for part 1 of this React performance quickwins series! Hope you could already apply some of them and make your React app even faster.

Let me know if these tips helped or you have other tips, you'd like to share.

This blog post was originally published on marcokuehbauch.com.

Head over there to let me know what you think and share it with others!

You can also find me on Twitter at @mkuehb.

Top comments (3)

Collapse
 
sgolovine profile image
Sunny Golovine

Something to watch out for when using a single useState hook, you want to pay attention and make sure it doesn't cause re-renders for the entire form whenever you update a single field. A project I worked on a while back suffered from massive performance issues due to re-renders.

Collapse
 
mkuehb profile image
Marco Kühbauch

That’s a great addition Sunny, thanks for that! And you‘re absolutely right. For a large form with a lot of input fields this could be an absolute performance nightmare. I think you always have to look closely and re-evaluate it for your individual usecase. For my small example here I think it’s alright.

Collapse
 
merri profile image
Vesa Piittinen • Edited

Google's performance tool lighthouse recommends not more than 1500 DOM nodes in total.

This recommendation is a bit dangerous. I've seen people remove DOM nodes for the sake of removing DOM nodes to comply, but then rendering them lazily later on causing effectively a bigger performance hit than just hydrating the HTML. You don't want to do that especially if those nodes are essentially static. If most of your page is static you can have as many DOM elements as you want. Why? Because the fact is you can render megabytes of HTML faster than you can hydrate a small React app. So you want to avoid active controlled DOM elements.

Tip: try to create your HTML structure / DOM tree so that you have large rarely rendered static paths, and try to keep the interactive parts as their own "widgets".