Sebastian Pieczynski's website and blog
Published on: yyyy.mm.dd
Published on: yyyy.mm.dd
Created with ❤️ by Sebastian Pieczyński © 2023-2024.
Published on: 11/16/2023
React is sophisticated tool and helps with a lot of low level stuff, but contrary to what AI people will tell you it cannot read your mind just yet.
When rendering the page React knows what it needs to re-render and more importantly what to destroy and add to the DOM. It does this by comparing previous state of the DOM tree to the current one. It creates it by comparing type, key (if it exists) and props of the components with the new type, key and props when the state changes. The difference between old and new versions tells it which parts of the application need to be re-rendered and how. This is simplified version of the reconciliation algorithm. You can read more about it in the legacy documentation.
React compares the elements by shallow comparison using Object.is
as it is safer way for React than using ===
. The differences between them is that Object.is
treats NaN
s as the same values and 0
and -0
as different. See MDN documentation on Object.is
if you want to know more.
Where keys
play an instrumental (you could even dare to say the key) role is the described reconciliation mode. When it happens React verifies if the type of the component changed and the old one should be removed (unmounted) from the DOM or if it needs to update the component (when props change). It uses createElement
function to add new nodes to DOM. See: rendering lists
To dispel a bit of magic from JSX let's take a peek at it's insides. The following examples are equivalent:
Direct call to createElement:
The first parameter of the createElement
is the type of the element to be rendered, then it's props
and children
.
Using JSX
syntax:
JSX
allows us to use much more readable syntax but as any abstraction hides some underlying concepts under the "magic" blanket. This use of createElement
is why we cannot have conditional logic inside JSX
but it also shows how React can understand what we want to create and how to render it.
Now where did I leave the key
s?
The key
allows React to know which components are stable and should not be removed from the DOM and re-created a new.
In our example above the list without the keys would re-render every element every time ex. when we added new item to the beginning of the list. With key
added to list items it would render only the added keys and not touch the rest.
What if we wanted to list all the properties of the book?
Notice what we return as the key inside the li
element. It's the key
of the book property (title, author publishedOn etc.).
Keen eyed will shout: "You said keys needed to be unique.". That's right. Inside a loop key between siblings of that list MUST be unique. Siblings is the key word here (pun intended). This is a very important point. You can repeat keys on the page but not within the same loop. See rules of keys .
Read the documentation about why React uses keys: in React docs
Changing the key of the component will force it to re-render itself. This can be useful sometimes when you MUST re-render a component but do not have control over it.
When rendering the list of sibling elements when there is not one element that can be assigned a key
we are not forced to close them in another div
or span
. In such case React will also warn us that it's an error (remember JSX
vs createElement
?) and it will not render the list without a single element encapsulating it inside a fragment like so: <>{children}</>
.
For example:
To render such structure we need to move it inside a React fragment, it's a special structure that allows us to render elements without creating another DOM node.
Now we have the same problem as before, we need a key but fragments are not allowed to have keys... Unless..
unless they are used as "full" components not shorthand of <></>
Ok so this might be just enough to show exactly why key
is important and how it affects rendering process in React.
You will notice that now response is outside of our component (as it should be), that Book
component was extracted and it is memoized with React.memo
so if the component does not change it's props
or key
React will skip rendering them even when state in the parent component changes. For our example we'll assume that these components are costly to recreate and we need to memoize them. By the way these are 1000+ pages books - let's assume they are heavy to carry even for React 😉.
If you want to play with it here's the link to CodeSandbox.
Styled example is just below, functionally they are the same.
Notice how removing key
from a Book
component forces React to re-render books after filtering even though it has not changed it's props or state. React just doesn't know that it's the same element without a key
.
Today we learned that:
keys
can lead to much more performant application by explicitly reusing DOM nodes instead of re-rendering them.Glad you made it this far and I hope you enjoyed another adventure with me.
PS. If you need some positive strength then You should read Brandon's books even if you are not into the epic fantasy. I highly recommend them!