Let’s see what happens under the hood.
At this point, we call setState to tell React that the state of this component will change.
React will then schedule an async call to the render method. Whether its five ms, or ten ms, we don’t know when it happens, but it will happen in the future.
The render function will return a new React element. That element comes from the JSX code.
1 2 3 4 5 6 7 8 9 10 |
render() { return ( <div> <span className={this.getBadgeClasses()}>{this.formatCount()}</span> <button onClick={this.handleIncrement} className={this.names()}> click me </button> </div> ); } |
As each render function produces a new React element, when our state changes, the incoming React element will be different than the previous React element. React will diff them, and then apply the change(s) to the real DOM.
For example:
So when we update the count to 1 in our state object, the data we access in formatCount() within the span tag gets updated.
As we know, each element (button, div, span..etc) is an element in our virtual DOM tree. They get their data from the state object.
And when that state object gets updated as the user plays around with the UI, the current virtual DOM gets scheduled (pushed onto a scheduler) to be rendered asynchronously.
React will diff them and see that the incoming virtual DOM have changed compared to the previous virtual DOM. In our case, our span’s data has been modified.
It will notice that our span is modified because that’s where we have used formatCount() to retrieve the updated count property from the state object.
So it will reach out to the real DOM, and update that span, in order to match what we have in the virtual DOM.
Go to the web page, Inspect Elements, and click on the ‘Increment’ button.
You’ll see that as you push the increment button, it calls
1 |
this.setState({ count: this.state.count + 1 }); |
which updates the span. It updates the span because it depending on this.state.count it will update the class name to badge-primary or badge-info, and also update the count integer. Thus, in your Inspect Element, nothing else in the DOM is affected. Only that span element.
How does React render Updates?
Explanation
render() function is the point of entry where the tree of React elements are created. When a state or prop within the component is updated, the render() will return a different tree of React elements. If you use setState() within the component, React immediately detects the state change and re-renders the component.
React then figures out how to efficiently update the UI to match the most recent tree changes.
This is when React updates its virtual DOM first and updates only the object that have changed in the real DOM.
Batch Updates
React follows a batch update mechanism to update the real DOM. Hence, leading to increased performance. This means that updates to the real DOM are sent in batches, instead of sending updates for every single change in state.
The repainting of the UI is the most expensive part, and React efficiently ensures that the real DOM receives only batched updates to repaint the UI.
Explanation
When you use React, at a single point in time you can think of the render() function as creating a tree of React elements. On the next state or props update, that render() function will return a different tree of React elements. React then needs to figure out how to efficiently update the UI to match the most recent tree.
There are some generic solutions to this algorithmic problem of generating the minimum number of operations to transform one tree into another. However, the state of the art algorithms have a complexity in the order of O(n3) where n is the number of elements in the tree.
If we used this in React, displaying 1000 elements would require in the order of one billion comparisons. This is far too expensive. Instead, React implements a heuristic O(n) algorithm based on two assumptions:
– Two elements of different types will produce different trees.
– The developer can hint at which child elements may be stable across different renders with a key prop.
The Diffing Algorithm – reconciliation
When diffing two trees, React first compares the two root elements. The behavior is different depending on the types of the root elements.
Elements Of Different Types
Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch. Going from a tag to img tag or from article tag to Comment tag or from button tag to div tag – any of those will lead to a full rebuild.
When tearing down a tree, old DOM nodes are destroyed. Component instances receive componentWillUnmount(). When building up a new tree, new DOM nodes are inserted into the DOM. Component instances receive componentWillMount() and then componentDidMount(). Any state associated with the old tree is lost.
Any components below the root will also get unmounted and have their state destroyed. For example, when diffing:
1 2 3 4 5 6 7 |
<div> <Counter /> </div> <span> <Counter /> </span> |
This will destroy the old Counter and remount a new one.
DOM Elements Of The Same Type
When comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes. For example:
1 2 |
<div className="before" title="stuff" /> <div className="after" title="stuff" /> |
By comparing these two elements, React knows to only modify the className on the underlying DOM node.
When updating style, React also knows to update only the properties that changed. For example:
1 2 |
<div style={{color: 'red', fontWeight: 'bold'}} /> <div style={{color: 'green', fontWeight: 'bold'}} /> |
When converting between these two elements, React knows to only modify the color style, not the fontWeight.