Recently I started learning the fundamental principles behind React. I was curious to learn more about the principles and techniques that influence the performance of React. With the help of ChatGpt and React code, I started getting under the hood.
React's performance is based on a few key principles,
Virtual DOM
React uses a virtual representation of the actual DOM, called a Virtual DOM. This allows React to compare the previous state of a component to the new state and only update the parts of the DOM that have changed.
(source: https://beta.reactjs.org/learn/preserving-and-resetting-state)
Reconciliation
It is the process that React uses to determine what changes need to be made to the actual DOM (Document Object Model) to align it with the virtual DOM (React's representation of the actual DOM).
Immutability
React uses immutability to ensure that the component state cannot be directly modified, which allows for better performance by avoiding unnecessary re-renders.
Lazy loading & Suspense
React's new feature, called Lazy loading & Suspense, allows developers to load only the needed components, which can help improve the application's performance.
Shallow compare
React uses a shallow comparison algorithm to determine if two objects or values are equal. This allows React to determine if a component's props or state have changed and only update the component if necessary.
Batching updates
React will batch updates together rather than applying them individually, reducing the time spent updating the DOM.
React Fiber
In this article, we will learn deep dive into algorithms behind Reconciliation.
React Fiber is a new reconciliation algorithm that was introduced in React v16. It is more fine-grained and flexible than the previous algorithm, allowing better control over the rendering process and improved performance. It builds upon the basic techniques used in the previous version of React's reconciliation algorithm, such as the Virtual DOM and keyed reconciliation. However, it also introduces new algorithms and data structures that provide more fine-grained control over the rendering process and improved performance. Here is the type defined in React codebase.
Keyed reconciliation
React achieves O(n) performance for reconciliation by using a technique called "keyed reconciliation". This means that React assigns a unique key to each element in the virtual DOM, and when it performs reconciliation, it uses these keys to match elements in the virtual DOM with elements in the actual DOM.
Using this approach, React can determine which elements have been added, removed, or moved in O(n) time instead of the O(n^3) time that would be required if it had to compare all the elements in the virtual and actual DOMs. This is one of the reasons why React can perform updates efficiently, even when dealing with large, complex UIs.
Priority-based rendering
React Fiber uses a priority-based rendering system, which allows certain updates to be given higher priority and processed more quickly. This allows for more critical updates, such as user input, to be handled with a higher level of responsiveness.
Coarse-grained updates
React Fiber uses a technique called "coarse-grained updates," which group similar updates together and apply them at once. This reduces the number of times the DOM needs to be updated, improving performance.
Incremental rendering
React Fiber uses an " incremental rendering technique," which allows updates to be spread out over multiple frames rather than applying them all at once. This allows the browser to handle other tasks, such as layout and painting, in between updates, resulting in smoother and more responsive updates.
Back-tracking
React Fiber uses a backtracking mechanism to recover from failed renderings. It allows React to retrace its steps, undo any changes made in the previous frame, and then retry the render with new data.
React Fiber uses a backtracking mechanism to recover from failed renderings. The specific implementation of backtracking in React Fiber is complex, and it's impossible to show you the exact code in this forum. However, the basic idea is that React uses a stack to keep track of the state of each component during the rendering process. React can pop the state off the stack when a component fails to render, undo any changes made in the previous frame, and then retry the render with new data.
Here is an example of how this might look in a simplified version:
class MyComponent extends React.Component {
state = {
count: 0
};
handleClick = () => {
this.setState(({ count }) => ({
count: count + 1
}));
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>Increment</button>
</div>
);
}
}
In this example, when the button is clicked, the component's state is updated with the new count value, and the component will re-render. The backtracking mechanism will come into play if the render function throws an error due to some unexpected state. React would undo the state change that caused the error and retry the render again.
It's important to note that React Fiber backtracking is an advanced feature that is not intended to be used directly by developers and is not exposed through the public API. The feature is only used by React internally to improve performance and stability.
Linked-list data structure
React Fiber uses a linked-list data structure to keep track of the different components that need to be updated and their respective priorities. This allows React to process updates efficiently according to their priority and reduces complexity.
Interruptibility
React Fiber allows the rendering process to be interrupted, in case of high-priority updates, such as user input. This allows React to switch between different rendering tasks and prioritize the most important ones.