Frontend developer from Czech republic
Published on 11/13/2018
React is an amazing library, which is used for creating user interfaces. When it comes to performance of react apps first thing that occur in my mind is Virtual DOM or maybe better a reconciliation process. This concept'll tell us how react updates the DOM.
Reconciliation is the process through which React updates the DOM.
As a developer we are creating tree of components, react then takes this tree, process it and we get a Virtual DOM that it's kept in memory. When there is an update in our application (e.g. change in state or props) react will take the updated Virtual DOM and compares it with the old one Virtual DOM, then decides what and how should be changed. This procedure is repeated all over again.
Also synced versions between Virtual DOM and "real" DOM are served by libraries such as ReactDOM.
React needs to be very fast at comparing those trees, so it uses heuristic algorithm with complexity of O(n), so this says for 1000 nodes we need 1000 comparasions.
This approach is used instead of state of the art algorithms, which have complexity of O(n\^3) => for 1000 nodes we need 1 bilion comparasions.
So with this rule, diffing from:
render() {
return(
<div>
<h1>Hello World!</h1>
<p>Welcome.</p>
</div>
);
}
To:
render() {
return(
<span>
<h1>Hello World!</h1>
<p>Welcome.</p>
</span>
);
}
Would destroy div element with all its children inside and made a new span with h1 and paragraph.
We have all come to this part when using react. This can be seen very often while iterating over an array and we return some kind of JSX as a respond.
renderFlags() {
return ["Private", "Property"].map((flag) => {
return <p key={flag}>{flag}</p>;
});
}
The key attribute is very important for react to indicate which of the children in a tree were changed and which stayed unchanged. However there is an important point.
renderFlags() {
// First render.
["Private", "Property"].map((flag, index) => {
return <p key={index}>{flag}</p>;
});
// Second Render
["Public", "Private", "Property"].map((flag, index) => {
return <p key={index}>{flag}</p>;
});
/*
First render returns:
<p key={0}>Private</p>
<p key={1}>Property</p>
Second render returns:
<p key={0}>Public</p>
<p key={1}>Private</p> ! Key changed from 0 to 1
<p key={2}>Property</p> ! Key changed from 1 to 2
*/
}
So if we add an element in the beginning of an array it shifts the index for rest of the elements. So react will mutate all of these children.
I can't really explain this easily, but there is a nice explanation in official docs.
With react 16 new reconciliation engine appeared. It improves performance of react apps and has many of wonderful features, but the concept is a little bit more complex, so if you want to learn about it on your own there is a nice post by Andrew Clark.
Another good sources to read: