Ever had a case when you type something in a React app, and it reacts super super slowly?

One of the reasons that may happen is unnecessary rerenders. Here’s how to find and fix them (a thread) ⬇️
1) First, note: *not all rerenders are bad*.

Rerenders aren’t bad per se. If a component is small, a rerender would be cheap and won’t really affect performance.

It only makes sense to optimize rerenders that actually make the app slower. How to find these?
2) My favorite way to do this is with React DevTools → Settings → “Highlight updates when components render”:
https://twitter.com/iamakulov/status/1275783591293857792

Now, every time anything rerenders, you’ll see it highlight with green/yellow/red. The hotter the color, the more frequently it rerenders.
Eg here, at AirBnB, every time you type into the “Destination” field, the whole header rerenders. If the header is complex, this can make the app slow!
3) Another useful tool is React Profiler. Unlike “Highlight updates” (which highlights components that are rerendering too *often*) it finds what’s slow.

Here’s how to use it.

1️⃣ Start the recording → do something → stop the recording. You’ll get a list of all rerenders:
2️⃣ In every rerender, you’ll see each component’s self time¹. The higher’s self time, the yellower’s the component.

(¹ — self time = the time it took for render() to execute.
useEffect()/componentDidUpdate()/etc are not included!)
3️⃣ And if you hover or click any component, you’ll also see *why* it rerendered.

(This has to be enabled in Settings → Profiler → “Record why each component rendered while profiling”.)
4) Now. You’ve found a component that rerenders too frequently or too slowly. How do you fix it?

There are a few common patterns where a component rerenders unnecessarily. Here’s each one.
5) Not memoizing a component.

By default, *every* component rerenders *every* time its parent rerenders. Even if its props don’t change.

This is fine in most cases (see pt. 1), but if it’s slow, you may want to wrap it with React.memo (or use React.PureComponent).
6) Passing a new function to a prop every time.

This is common with event listeners:

<Button onClick={() => ...} />

() => ... creates a new function every time. This makes `onClick` change with every rerender.

To fix this, wrap the function with `useCallback`.
7) Passing a new object or array every time.

There’re a few cases when this can happen. Here’s one:

<Avatar user={user || {}} />

If `user` is falsy, `Avatar` will receive a new object on every render.

To fix this, store the {} outside of the component – to keep it constant.
Here’s another:

<Users ids={ http://users.map (user => http://user.id )} />

This calls `.map()` on every rerender – and `.map` returns a new array every time. This makes `Users` rerender every time.

To fix this, wrap the `.map` operation with `useMemo`.
8) Passing a new React element every time:

<Header icon={<HamburgerIcon />} />

This recreates the <HamburgerIcon /> element on every render.

To fix this:
— pass HamburgerIcon instead of <HamburgerIcon />
— or store <HamburgerIcon /> outside this component – to keep it constant
9) Returning a new object/array in react-redux’s useSelector():

const smth = useSelector(state => [state.a, state.b])

react-redux rerenders the component when `useSelector` returns a new value. If that happens every time (like above), the component will also rerender every time
To fix this,
a) split the single selector into multiple selectors, or
b) use `createSelector()` from reselect.
10) Returning a new object/array in react-redux’s mapStateToProps.

The previous issue affects `mapStateToProps` as well. The only difference is that react-redux compares fields of the returned object, not the object itself.

The fix is similar!
11) “Should I go over my codebase and fix all these issues now?”

No. You don’t need to optimize every unnecessary rerender. A lot of rerenders are cheap and are not an issue at all.

Find what’s slow. Then optimize that.
12) “But why can’t I just wrap every component with React.memo()?”

You can, but you shouldn’t! React.memo() (or PureComponent) has a cost: it compares props every time anything changes.

This may get expensive. And this increases complexity: https://twitter.com/kentcdodds/status/940448899344433152
You can follow @iamakulov.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled:

By continuing to use the site, you are consenting to the use of cookies as explained in our Cookie Policy to improve your experience.