React is one of the most popular javascript libraries for building user interfaces. The main advantage of react is when an update happens it only updates the DOM elements that need to be updated. It achieves this by using virtual DOM which is nothing but an internal representation of our UI.
Letβs try to understand how react manages virtual dom and how it evolves the virtual dom structure to support concurrent version.
For those of you who just want to get a gist of react internals I highly recommend to watch Lin Clark 's video of react fiber.
Let's start with the basics.
JSX
React introduces JSX for better developer experience. JSX is a syntactic sugar for React.createElement(type, props,...children) or _jsx/ _jsxs
For example when we define a component like this
the babel compiler transpiles the above code to
And in current versions *_jsx will be there instead of React.createElement. Both performs the same. _jsx was created for improving our React development experience.*
React.createElement returns a JS object describing the node and its properties.
Eg:
const profile = <h1 className='welcome'>Hello World </h1>
would return something like this
{
type: 'h1',
props: {
className: 'welcome',
children: ['Hello World']
},
ref: null,
key: null,
$$typeof: Symbol(react.element)
}
The returned element has the following properties.
type
The type of the element
If it is a custom react element the type would be the reference of the function/class of the element.
If it is a host element(like div in DOM) the type would be a string oneprops
The props of the element like its children or colour etc.,$$typeof
This key is just to ensure the component is created from react to prevent XSS. Dan Abramov has explained the importance of key in this article. Do check that out.key
It is used to uniquely identify the element in case the type of the element is not enough to identify it uniquely.ref
Reference of the instance if it is a custom react element or the dom node reference if it is a host element
Using this object builds a tree-like data structure i.e virtual DOM and stores it in memory.
Reconciliation
When an update occurs react uses the previously constructed virtual dom and generates a new tree based on the update. React compares these two versions and updates the elements in the DOM efficiently. This process is called Reconciliation
But if react actually compares every node/object in the tree with its previous version, it'd be time-consuming right?. To avoid this react uses a heuristic algorithm that is based on the following rules.
Elements of different types will generate different branches and subtrees
Eg:
<div> Hi I am a div </div>
If the above element changes to the following element,
<span> Hi I am a span </span>
Then react will delete the div and appends span into its parent. Similarly, if
<Hello/>
/* Changes to */
<Bye/>
Then react will unmount Hello element or its instance and mounts Bye
Elements of the same type
If the type of both the elements is the same it will check whether the props changed. If it is changed it will then modify the props of the element. Simple!
Eg:
<Message text='Hi'/> ==> <Message text='Bye'/>
will modify the text property from 'Hi' to 'Bye' in the Message instance.
<div className='active'/> ==> <div className='inactive'/>
will modify the class of the div element
Elements of the same type but with different properties
Let's say we want to list a bunch of fruits like the following
<ul>
<li>Banana π</li>
<li>Cherry π</li>
</ul>
And due to an update, we append an entry to the first child of ul element now our component will look like this.
<ul>
<li>Apple π </li>
<li>Banana π</li>
<li>Cherry π</li>
</ul>
Since react is working on our above-said rules, react will do the following
- Checks ul element type first. Since it is the same in both versions it proceeds to check its props. Since children are changed(Reference will be changed) it starts to check every li element
- Checks the first element of ul element i.e li in both version since the text content is changed from Banana π to Apple π, it updates the change to the DOM. Then it proceeds to check the next element
- Now it checks the second li element and updates the text change to the DOM from Cherry π to Banana π
- Then it finds out that there is a new element created at the bottom. So it creates a new li element and appends the element to the ul element.
Have you noticed anything unusual in this behaviour? Apple is the one that was appended to the first but react is updating the other two items unnecessarily, right? i.e Instead of performing a single update, react is performing three updates.
To avoid this react has introduced the key to uniquely identify each element.
Elements of the same type but with different keys will be uniquely identified and updated now. So if we introduce key to the above example
<ul>
<li key={1}>Banana π</li>
<li key={2}>Cherry π</li>
</ul>
to
<ul>
<li key={0}>Apple π </li>
<li key={1}>Banana π</li>
<li key={2}>Cherry π</li>
</ul>
Now react while comparing the first element knows that Apple π is newly added to the list and performs that single update.
Please do note that using index as key may create some issues especially when each of your components has an internal state and reorders between list is pretty common. Check this codepen for more details.
In the next article, we will try to create a naive react algorithm or the algorithm which relies on recursion by implementing the above concepts.
*P.S: This blog-series is largely inspired from this amazing blog written by Max Koretskyi *
Top comments (0)