How Does React Work?
Understanding the Behind-the-Scenes Functioning of React
Hey! Ever wondered what's happening under the hood when you're building your React apps? Well, buckle up, we're about to take a wild ride through the fascinating world of React's rendering process.
This article delves into the intricacies of React's rendering process. In this article:
Components, Instances and React Elements
The Rendering Process
Triggering a Render
The Render Phase
The Virtual DOM
Reconciliation: React's Game of Spot the Difference
The Commit Phase
React DOM
Browser Repaint
Optimization
Components, Instances and React Elements
Before diving into the rendering process, let’s recap a little bit about the relationship between components, component instances, and React elements:
Components are the building blocks of React applications.
Component instances are the actual implementations of these components in the application, holding state and props.
React elements are the results of JSX compilation, produced by
React.createElement()
calls.If you want to understand this concept better, I have explained this in detail here:
The Rendering Process
Alright, now for the main attraction! React's rendering process can be divided into three main phases:
Triggering a render
Render Phase
Commit Phase
Triggering a Render
There are only two ways to trigger a render in a React application:
Initial render: The first time the application runs.
State update: When a state update occurs in one or more component instances.
It's important to note that when a render is triggered, it affects the entire application, not just a single component. However, this doesn't mean the entire DOM is updated.
Interestingly, renders are NOT triggered immediately after a state update. React schedules the render for when the JavaScript engine has available processing time, usually just milliseconds later. In some cases, like multiple setState calls in the same function, renders may be batched for efficiency.
But wait, what about the prop changes?
A common misconception in React development is that changes in props directly trigger re-renders. However, for non-memoized components, prop changes are not the determining factor for re-renders.
Consider the following:
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<Child prop={count} />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
function Child(props) {
console.log('Child rendered');
return <div>{props.prop}</div>;
}
In this scenario, Child will re-render whenever Parent re-renders, regardless of whether the count changes. This occurs because the re-render of Parent necessitates the re-evaluation of its entire render tree, including Child.
Prop changes become relevant when employing memoization techniques such as React.memo or useMemo:
const MemoizedChild = React.memo(Child);
function Parent() {
// ... same as before ...
return (
<div>
<MemoizedChild prop={count} />
{/* ... */}
</div>
);
}
The Render Phase
Now, we enter the render phase. Here, React calls our component functions and figures out what needs to change in the DOM. But here's the kicker: This phase is purely computational and doesn't produce any changes on the screen yet!
At first, React will go through the entire component tree, take all the component instances that triggered re-render, and render them which simply means to call the corresponding component functions from the code.
So, here’s how it’s done :
React calls component functions.
It determines what changes need to be made to the DOM.
No actual DOM updates occur during this phase.
This is a common misconception - in React, "rendering" is not about updating the DOM or displaying elements on the screen. It's an internal process that doesn't produce any visual changes.
Here's another interesting fact: when a state update occurs, React doesn't just re-render the changed component. It also re-renders all the child components, even if their props haven't changed. But don't worry! This doesn't mean React is wastefully updating your entire UI. It's simply creating a new Virtual DOM, which is as computationally very inexpensive.
The Virtual DOM
You've probably heard the term "Virtual DOM" tossed around in React discussions. But what exactly is it? In essence, the Virtual DOM is a lightweight, JavaScript object representation of your component tree. It's React's way of keeping a blueprint of your UI that can be quickly created and manipulated without touching the actual DOM.
When React needs to update your UI, it doesn't immediately start repainting your browser window. Instead, it first creates a new Virtual DOM, which is incredibly fast and efficient. So, Virtual DOM is the tree of all the react elements created from all instances in the component tree. It is cheap and fast to create multiple trees.
Reconciliation: React's Game of Spot the Difference
Updating the entire DOM whenever the state changes somewhere in the app would be inefficient and wasteful because writing to DOM is relatively slow and needs to be carried on for only a small part of the DOM that needs to be changed.
React uses as much of the existing DOM as possible by using reconciliation.
Reconciliation is deciding which DOM elements actually need to be inserted, deleted, or updated, to reflect the latest state changes.
The current reconciliation engine in React is called Fiber. Fiber creates and maintains something called the Fiber tree. Think of it as React's own personal journal, keeping track of component states, props, side effects, and more. During this, Fiber walks through this tree, comparing the old and new versions. It's looking for any changes that need to be made to the actual DOM. This process is called "diffing", and it's incredibly meticulous.
If you are interested in knowing how this whole process is carried out, look for my next article where I will describe the process in detail.
The result of all this detective work is a list of effects - essentially a to-do list of DOM updates. But remember, at this point, React hasn't touched your actual UI yet. This list of the impact will be used in the next phase - the commit phase - to actually update the DOM.
The Commit Phase
Once React knows how to update the DOM, it moves to the commit phase. This is when the actual DOM manipulation happens:
React DOM (a separate library) takes over to apply the calculated changes.
DOM elements are inserted, deleted, or updated as needed.
This phase happens synchronously to ensure your UI stays consistent.
This phase is responsible for what we traditionally think of as "rendering" - updating the actual DOM.
After React completes the commit phase, the browser notices the DOM updates and repaints the screen. This final step, while not part of React, is what produces the visual changes users see.
But here's the kicker - React doesn't just update the components you changed. React re-renders all the child components too, just to be safe.
React DOM
Here's a fun fact: React itself never touches the DOM. That job falls to React DOM, the hardworking implementer who takes React's instructions and makes them a reality.
And get this - React isn't just for web applications. With the right renderer, you can use React to create mobile apps, videos, or even Word documents.
This means you can use React to build:
Mobile apps with React Native
Desktop apps with React Desktop
VR experiences with React 360
And even create videos with Remotion and more
It's like a Swiss Army knife of UI development!
Browser Repaint
Following the commit phase, the browser notices the DOM updates and repaints the screen making your beautifully crafted UI visible to the world. This step is a standard browser operation and is not controlled by React.
Optimization
Now, here's where it gets really interesting. Remember how I said a render is triggered by a state update? But the React doesn't jump into action immediately. Instead, it schedules the render for when the JavaScript engine has some downtime. React even batches multiple state updates together. Imagine you have several setState
calls in the same function. React, being efficient, might batch these updates and perform a single render. This process is called Scheduling and Batching:
scheduling: Renders are scheduled for when the JavaScript engine has available time.
batching: Multiple state updates may be batched together, resulting in a single render.
This optimization helps improve performance by reducing unnecessary renders.
Wrapping Up
React's rendering process is a testament to the thought and engineering that goes into making our lives as developers easier.
Understanding how React works has tremendously helped me become a better developer, I hope you find it helpful as well.
Keep an eye out for our future articles where we'll delve deeper into advanced React concepts and best practices. Until then, happy coding! 🚀