Alex Sidorenko

A Visual Guide to React Rendering - Props

July 26, 2021

The Child component is wrapped in memo. Its props don’t change. Why does it still re-render when Parent renders?

There are two types of values in javascript. Understanding the differences between them gives you Jedi powers in controlling component rendering.

Yoda

This article is a second chapter of "A Visual Guide To React Rendering". If you haven't yet, check out the first chapter It always re-renders.

Primitives

The first type of value is primitive. Let’s try to pass a primitive value as a prop to the Child.

The Child is wrapped in memo. This means it will only re-render when its props change. The way memo determines if props changed is by shallow comparison prevProp === nextProp. Since "Alex" is a string, which is a primitive value, this comparison will return true, and the component won’t re-render. You can check it by yourself, paste this in your browser console "Alex" === "Alex".

Non-primitives

The second type of value is non-primitive. Here we pass the object as a prop. The object is a non-primitive value. So why does a non-primitive value makes the Child re-render?

Remember, to decide if the props changed, memo does the shallow comparison. When you compare two non-primitives like that {display: "flex"} === {display: "flex"}, the result will always be false. You can check it in your browser console.

In JavaScript, objects are a reference type. Two distinct objects are never equal, even if they have the same properties. Only comparing the same object reference with itself yields true.

MDN - Comparing Objects

References

When we say a variable stores a reference, we mean that it points to some value in memory. Here’s the visualization of the reference comparison.

a = {display: 'flex'}; b = {display: 'flex'}; a === b // false; a === a // true

Although it seems that a and b identical, each of them stores a reference to a different value. The same way {display: "flex"} === {display: "flex"} compares two different values in memory.

Check out Kyle Simpson’s You Don’t Know JS (Values vs References) for more info.

Knowing all that, how do we prevent the Child from getting re-rendered? The easiest solution is to declare the variable outside of React component and pass it as a prop.

This way, when memo compares props, it will do style === style // true instead of {display: "flex"} === {display: "flex"} // false

Notice that it wouldn’t work if we declare the variable inside the component:

That’s because every time Parent renders, the style variable gets redeclared with a new reference pointing to a new value.

Anonymous functions

There are other non-primitive values like arrays and functions. It’s a common pattern in React to pass an anonymous function to an event handler like this:

It’s important to understand that since the function is a non-primitive value, the same rules of comparison apply. And if we want to prevent the Child from getting re-rendered, we need to provide the same reference as the prop.

Memoization

In real-world, most of the time, when you deal with non-primitive values passed as props, they rely on the state or other props of a component:

In this case, it’s impossible to declare a variable outside of React component. It must be declared inside. But how do we prevent a variable from being redeclared and reassigned new reference on every re-render? That’s why React has useMemo and useCallback hooks to memoize props. But this is a topic for the next part of “A Visual Guide To React Rendering”

Next chapter

A Visual Giude to React Rendering - useMemo