We will discuss some under the hood concepts like
- React elements, components and component instances
- Reconciliation
- Diffing Algorithm
React Elements
Let’s consider a very basic React component -
What is we do a console.log(App())
after this ?
We should be able to see an object in the console -
Whenever this React function is called -
- The JSX elements get converted to React.createElement() calls.
- The reason why we import React is because we need to tell the bundler that this conversion needs to happen.
- Each of the
React.createElement
calls returns the type of object showed above. - Each such object can have multiple objects within its children.
A JSX such as above gets converted into a React.createElement() call which looks like this -
React.createElement("div", null, "App component");
wherein,
- The first param is the type.
- The second param is the props.
- The third param is the children.
And that’s how we get a React Element.
React Component
React component is a class or a function that outputs a element tree.
It can be the return value of the function in case of React Functional Components, or it can be return value of the render method in case of React Class Components.
A React Component also takes inputs in the form of props.
The way we use components in React is via injecting them in JSX in this format - <App />
.
What if I were to console.log this as well -
This is also a React Element but with a weird type. Which basically means all react components are react elements.
The type property of this object is a function which is nothing but our React component.
Whenever <App />
is called in the code -
- In case of class components, React creates an instance of the class and call its render method with the provided props.
- In case of function components, React calls the function directly with provided props.
Component Instance
In the above case whenever <App />
is encountered, along with calling the function, React creates a component instance of that component.
React handles the state, and the lifecycle of the component instance. Different hooks and methods can be used to tap into different lifecycle phases.
Reconciliation
All React does, is produce a tree of elements.
It starts by creating React elements from the top and then moving recursively to the bottom. If a component is encountered in between, React calls it too.
The tree of elements which React produces is actually a plain JavaScript object. And that’s why this process is very fast. This JavaScript object which contains the React tree is called the Virtual DOM.
At the time of initial render, there is no option but to fully insert the tree onto the DOM.
In the lifespan of the component, it can probably happen that its state might change, which needs to be reflected back onto the UI.
In such cases, React creates a new tree of elements with the changes to the state. But now, React can’t just re-render the entire new tree onto the DOM.
It needs to find minimum number of steps that can be taken to convert the old tree to the new tree, as DOM manipulations can be pretty heavy. React does this with the use of its Diffing Algorithm.
Diffing Algorithm
Usually comparing 2 trees should take a complexity of O(n^3^). But React’s diffing algorithm is able to do this in O(n) complexity or n operations.
This is possible only because of 2 assumptions Diffing algorithm makes -
- Different elements (different types) will output a different sub tree.
- When we have a list of child elements which might change often, we will provide a unique key to all of them.
That’s why key is a special property. And also because of this, code which uses index as the key to its elements might result in bugs.
instead, item.id
or any unique identifier of that particular item can be passed as the key.
3 scenarios that Diffing algorithm handles -
- Different elements - In that case, a new tree is re-rendered in its place, unmounting all the components used in that old tree - states and lifecycles of those components are lost.
- Same Element, different attributes - In that case, React just updates the attributes, and no state is lost.
- Children are updated - React compares elements on the same position, and doesn’t do anything till it finds a mismatch and then re-renders from there.
but, what if new elements are added to the start of the list ? In that case, React will have no option but to re-render the entire component tree.
But, this can be avoided by passing a unique key to all those children, so that React knows which elements are still the same and which elements are new.
Rendering
React doesn’t do any kind of rendering! That’s the job of renderers.
All React does is -
- lets you define your components.
- differentiate between 2 component trees (Diffing algorithm)
React doesn’t even know that these elements or components might end up on a browser screen! And that’s why it’s platform independent, and can be used with different renderers.
In the case of web development, ReactDOM is the renderer, and actually does the reconciliation by communicating with React.
You can create your own renderer as well, by using react-test-renderer package.