A ReactElement
is an implementation of the concept called the Virtual DOM (VDOM
). The VDOM
is a JavaScript object that exists in memory and can be created using the React.createElement
function. The elements created by this function are just regular JavaScript objects. To simplify it, the following code example can be used:
React.createElement = (tag, config, children) => {
//
return {
tag: tag,
className: config.className,
props: {
children: children
},
dom: null
}
};
React.createElement("div", {className: "root-layout"},
[React.createElement("h1", {className: "title"}),
React.createElement("h5", {className: "sub-title"})]
);
In the traditional DOM, whenever there is a change in a node, the entire DOM Tree had to be recalculated. React, however, only performs operations on the necessary nodes in the VDOM and then updates the real DOM in one go. Additionally, React reduces the effort needed by users to handle node manipulation while navigating the DOM Tree.
The function that reflects the VDOM to the actual DOM, ReactDOM.render
, can be simply expressed as:
ReactDOM.render = (virtualDom, realDom) => {
//
realDom.appendChild(virtualDom.dom);
};
Inserting a DOM node into a tree is called "Mounting," and this process can be roughly described as:
React.mountElement = (element, parentDomNode) => {
//
const domNode = document.createElement(element.tag);
element.dom = domNode;
if (element.className !== undefined) {
domNode.className = element.className;
}
parentDomNode.appendChild(domNode);
};
A simple ReactElement does not have a state. React provides a Component
class, which allows for state management. The Component
class has an interface like the one below:
class Component {
constructor(props) {
//
this.props = props || {};
this.state = {};
}
setState(partialState) {
// merge states and update component.
}
render() {
// will be overridden.
}
}
The constructor
method of the Component
class is called after the component is created but before it is mounted. This method includes tasks for initializing the component, such as passing external parameters (props) as constructor arguments. For React components that inherit from this, the constructor method must be called with the props as shown below:
class UserSpecifiedComponent extends React.Component {
constructor(props) {
super(props);
// do something you want to do after component creation.
// for example, initialize state.
this.state = {
name: 'default name'
}
}
}
The setState
method takes as a parameter either a value representing the changed state or a function that returns the new state value. The shouldComponentUpdate
method compares the changes and updates the component accordingly. More on shouldComponentUpdate
will follow.
setState(partialState) {
//
const prevState = this.state;
let changedPartialState = null;
if (typeof partialState === 'function') {
changedPartialState = partialState(prevState, this.props);
} else {
changedPartialState = partialState;
}
const nextState = Object.assign({}, prevState, changedPartialState);
if (shouldComponentUpdate(this.props, nextState)) {
updateComponent();
}
}
React documentation recommends initializing the state in the constructor method and modifying the state using the setState
method thereafter. The reason for this becomes clear when you understand what happens in setState
.
If you change a state variable directly outside the constructor, unless you explicitly call a method to update the component (like forceUpdate()
or this.setState()
), the change will not be reflected visibly due to the lack of re-rendering. The reason you modify variables directly in the constructor is that the constructor runs before the render
method, so there’s no need to call render
again. Also, because setState
operates asynchronously, the state will not be initialized during the first render
call after the constructor is executed.
React components provide Lifecycle Methods, which are abstract methods called based on changes in the component’s state. We can override these methods to specify what actions should be performed during different stages of the component’s lifecycle. The lifecycle is generally divided into three stages: Mount, Update, and Unmount.
As mentioned, Mount refers to inserting VDOM nodes into the DOM Tree. Unmount is the process of removing nodes from the tree. Update, as expected, involves reflecting changes in the nodes.
The Component
Lifecycle Methods interface can be summarized as follows:
componentDidMount() {
/**
* Called immediately after the component is mounted.
* Overriding this method will trigger a re-render if the state is modified.
*/
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
/**
* Called when a re-render is triggered by a change in props or state.
* `Component` always returns true.
* `PureComponent` performs a shallow comparison of props and state and returns the result.
* If this method returns false, no re-render occurs.
*/
}
componentWillUnmount() {
/**
* Called immediately before the component is removed from the DOM.
* This is where you clean up tasks such as canceling network requests or clearing up data created in `componentDidMount`.
*/
}
componentDidCatch(error, errorInfo) {
/**
* Handles exceptions.
*/
}
- A shallow comparison involves checking equality. For simple values like numbers or strings, it compares the values directly. For objects, it compares their references, not their attributes. By returning
false
fromshouldComponentUpdate
based on specific conditions, unnecessary re-renders can be avoided.
Thank you.