DEV Community

Cover image for How React Works Under The Hood
Kingsley Ubah
Kingsley Ubah

Posted on • Updated on • Originally published at ubahthebuilder.tech

How React Works Under The Hood

How does React actually work beyond the syntactic sugar?

This question has dominated my thoughts for a very long time. My curiosity led me into carrying out a deep research on React, and my findings were quite interesting.

My quest to demystify React has turned out to be one of the most eye-opening things I have ever undertaken.

So if you also interested in finding out how React really works under the hood then you’re at the right place.

In this article, I am going to share some of my interesting discoveries about the inner workings of React, in regards to the rendering process.

HTML, The DOM and Essence of React

We all know that a web page is structured using HTML. Hence, web pages are basically HTML Documents.

A HTML document can be modified through an API called the DOM, using methods like querySelectorAll(), getElementsById() and so on.

After modification, the web browser has to re render the page to reflect those changes.

However, this process is very expensive for the web browser to undertake. So if you have a page which changes regularly (aka a dynamic web page), then directly manipulating the DOM would be inefficient.

In fact, it is slower to repaint the DOM than it to create 10,000 objects with JavaScript.

This fact predicates the existence and usefulness of React as a library.

React provides a declarative way to create user interfaces. In other words, you don’t have to specify how exactly you want your web page to be rendered, or how the elements should be inserted. Instead you are more concerned with what elements to create, and describing how they should look and behave.

React Elements are just Objects

You may or may not have known this, but React is just a tree of JavaScript objects.

For example, consider the following functional component:

const Title = () => {
    return (
     <div>
         <h1 id=title> Hello World </h1>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

In React, a component is basically a class or function that describes the UI which you eventually want to create.

A React element is basically a description of the UI which will be ultimately inserted into the DOM. Hence, a React element is not a DOM element until the render() method of the ReactDOM library is called.

When a component is called from a Parent, React will call the render() method on that child element and return a React element, which is just a plain object with certain properties.

For example, calling the above functional component actually returns the following object.

{
  type: div,
  key: null,
  ref: null,
  $$typeof: Symbol(react.element),
  props: {
    children: {
      type: h1,
      key: null,
      ref: null,
      props: {
        id: title
        children: Hello World!
      },
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The object contains certain properties.

  • type: This is a reference to the type of tag used. If an in-built DOM HTML element (such as main, div etc) is used, then the type points to the string representation of that DOM element. But if you import a custom React component using the typical import ... syntax, then you are referencing to a component element.

  • key: This property is used to identify an element amongst other children using unique values. This is usually when iterating over a list of children elements.
    Ref: a reference to an actual DOM node.

  • typeOf: The value of this property is always a symbol. Symbol is a JacaScipt data type introduced in ES6. The object takes in a value and returns a unique symbol. In the case of React, the Symbol object takes in a react.element. This is a protection feature against cross-scripting attacks. The feature is used a identify React elements to avoid scenarios where malicious values is passed to React.

  • props: This contains all of the elements children. If the component has multiple children, then the children property will be an array instead of an object. Each object has the same set of properties.

Now, this object is what we call the virtual DOM.

The process of building up these objects is significantly cheaper than directly writing to the DOM. So instead of making direct modifications to the DOM, we can create a virtual DOM and make modifications on that object tree instead.

React creates a tree of elements whenever the render function is called.

Reconciliation

Reconciliation houses the diffing algorithm which determines what part of the tree we should replace.

In other words, this is how React reconciles the DOM tree with the React element tree when a change is made.

The diffing algorithm is the way which we can tell difference between the two trees and determine what parts of the tree we need to replace.

One important behaviour of React is how it reacts to type change on the top-level (root) element.

In such a case, React tears down the whole element tree and builds up a new tree.

For example if the div tag were to change to a span tag, then React will destroy the entire tree along with the DOM nodes. The following will also happen:

  • All old component instance (in div) will receive componentWillUnmount and equivalent useEffect hook

  • New component tree will be built up with span as root element.

  • React will start rerendering again

  • New nodes will inserted to the DOM

  • New components will receive componentWillMont and then ComponentDidMount and their useEffects hook will run

  • The old props and state (for div) will be discarded

If there is only a property change, then React will only update the changed property, and not tear down the entire tree.

So assuming we are moving from one product item to another product item as in the code

<ProductList>
     <Product id={5} /> // Product Five
</ProductList>
Enter fullscreen mode Exit fullscreen mode
<ProductList>
     <Product id={6} /> // Product Six
</ProductList>
Enter fullscreen mode Exit fullscreen mode

React will maintain the same component instances, pass in a new id props and then rerender so we navigate to a different page.

Children

Suppose we have a list of and we insert a new movie to the beginning of the list.

<ul>
<li> First item <li>
<li> Second item <li>
</ul>
Enter fullscreen mode Exit fullscreen mode
<ul>
<li> New First item <li>
<li> First item <li>
<li> Second item <li>
</ul>
Enter fullscreen mode Exit fullscreen mode

React has no way of knowing where the change happened.

As a result, React would tear down the tree and rebuild a new tree, but this is not efficient.

Instead, you should pass a unique value to the key property of each child.

React will recursively check for the unique values of the key properties and compare them. That way, it will know where on the list to insert the new item.

<ul>
<li key=first> First item <li>
<li key=second> Second item <li>
<ul>
Enter fullscreen mode Exit fullscreen mode
<ul>
<li key=new-first> New First item <li>
<li key=first> First item <li>
<li key=second> Second item <li>
</ul>
Enter fullscreen mode Exit fullscreen mode

Rendering to the DOM

import ReactDOM from 'react-dom'
import App from "./App.js";

ReactDOM.render(
     <App />,
     document.getElementById("root")
);
Enter fullscreen mode Exit fullscreen mode

This process triggers the reconciliation process which builds up the DOM tree, the React element tree and the entire diffing process. Then React eventually inserts the React Component tree into the Browser DOM.

Wrapping Up

We have seen that React Elements are just regular objects. For all nested component, React generates a tree of objects which makes up the virtual DOM.

When then make updates on the virtual DOM through a process known as reconciliation.

P/S: Get weekly roundup of the best articles on web development by signing up to my newsletter

Discussion (7)

Collapse
fjones profile image
FJones • Edited on

So if you have a page which changes regularly (aka a dynamic web page), then directly manipulating the DOM would be very inefficient.

Sort of. Directly manipulating the DOM is highly efficient - after all, at the end of it all, React still has to do that on top of the VDOM handling. It's that naively manipulating the DOM without checking for optimizations (e.g. reordering elements, moving nodes, ...) is very inefficient.

React does this by effectively determining the minimal operation necessary to transform the prior DOM to the new DOM according to the changes tracked in the VDOM.

(As a result, various VDOM-less frameworks have sprung up recently as well, which still determine minimal operations, but without relying on a VDOM representation)

Collapse
ubahthebuilder profile image
Kingsley Ubah Author

Thanks a lot for pointing that out.

Collapse
mustapha3341 profile image
arewa_coder

Simple and straightforward explanation my guy. Keep up the good work. I really enjoyed this article.

Collapse
ubahthebuilder profile image
Kingsley Ubah Author

Thanks a lot. I'm glad you enjoyed it!

Collapse
gauravbehere profile image
Gaurav Behere

Article requires proof reading for typos. Great content otherwise. Keep posting such cool stuff.

Collapse
ubahthebuilder profile image
Kingsley Ubah Author

Thank you.

Collapse
dim0147 profile image
Bon

Do you know about lane in react when we update the state?