Aro Andriamaro

Software Developer

Quick tip to reduce React component re-render

Desk with computer desktop
Generated with AI

To begin with, re-render is not a big issue, slow render is. But if we can reduce re-renders easily, let’s not private ourselves.

Contrived example of a re-render

Let’s take a look at an App component that renders a list of suggested Gym club nearby and an input form that allows users to search for workout routine.

import {useState} from 'react'
const App = () => {
const [search, setSearch] = useState('')
const [workoutList, setWorkoutList] = useState([])
return (
<main>
<SuggestedGymClub />
<h1>Looking for a workout routine ?<h1>
<form onSubmit={async e => {
e.preventDefault()
const res = await fetch('/api/routine', {
method: 'POST',
body: JSON.stringify({search})
})
const data = await res.json()
setWorkoutList(data)
}}>
<label htmlFor='search'>Workout name</label>
<input
placeholder='Full body'
value={search}
name='search'
id='search'
/>
<button>Research</button>
</form>
<ul>
{
workoutList.map(workout => {
return <li key={workout.id}>{workout.name}</li>
})
}
</ul>
</main>
)
}

The problem with this code is that every time a user searches for a workout, SuggestedGymClub re-renders.

If SuggestedGymClub is a big and expensive component to render, it can cause bad performance for the app. So why does it re-render anyway ?

Because App component re-renders whenever search or workoutList change. When a parent component re-renders, all of its children re-renders as well, no matter what.

The solution to prevent re-renders

In our simple case, the solution is pretty simple. If we want SuggestedGymClub component not to re-render, we have to prevent App from re-rendering.

For that, we can extract all the logic of workout searching into its own component.

import {useState} from 'react'
const SearchForWorkoutRoutines = () => {
const [search, setSearch] = useState('')
const [workoutList, setWorkoutList] = useState([])
return (
<h1>Looking for a workout routine ?<h1>
<form onSubmit={async e => {
e.preventDefault()
const res = await fetch('/api/routine', {
method: 'POST',
body: JSON.stringify({search})
})
const data = await res.json()
setWorkoutList(data)
}}>
<label htmlFor='search'>Workout name</label>
<input
placeholder='Full body'
value={search}
name='search'
id='search'
/>
<button>Research</button>
</form>
<ul>
{
workoutList.map(workout => {
return <li key={workout.id}>{workout.name}</li>
})
}
</ul>
)
}
const App = () => {
const [search, setSearch] = useState('')
const [workoutList, setWorkoutList] = useState([])
return (
<main>
<SuggestedGymClub />
<SearchForWorkoutRoutines />
<h1>Looking for a workout routine ?<h1>
<form onSubmit={async e => {
e.preventDefault()
const res = await fetch('/api/routine', {
method: 'POST',
body: JSON.stringify({search})
})
const data = await res.json()
setWorkoutList(data)
}}>
<label htmlFor='search'>Workout name</label>
<input
placeholder='Full body'
value={search}
name='search'
id='search'
/>
<button>Research</button>
</form>
<ul>
{
workoutList.map(workout => {
return <li key={workout.id}>{workout.name}</li>
})
}
</ul>
</main>
)
}

For experienced React developers, this seems pretty obvious. But it can be easily overlooked when you write big component and you loose track of what state is shared or not.

So next time you notice performance issue in your app, I think looking for this simple state location issue first can save you a lot of debugging.