Telerik blogs

Let’s discuss how rendering works in React to aid you in developing highly optimized and performant React apps.

React is an open-source frontend JavaScript library for building user interfaces with a component-based architectural approach. It allows us to split user interfaces into independent, reusable pieces, which are then thought about in isolation.

React is a popular library that has a community of millions of developers. However, many React developers do not understand how rendering works in React. The general notion is that we write components that return JSX (JavaScript XML). The JSX is then somehow converted into actual HTML DOM elements displayed on the screen.

Understanding React’s rendering behavior aids in developing highly optimized and performant React apps, and in this article, we will discuss the basics of how rendering in React works.

React Elements and Components

React elements are the smallest building blocks in React applications. They are plain JavaScript objects that describe what you want to see on the screen, so we do not have to worry about creating those objects as React adopts an abstraction layer that handles that with JSX (JavaScript XML).

JSX is a syntax extension to JavaScript, which gets converted to React.createElement() function calls that evaluate to JavaScript objects. Unlike the browser DOM elements, React elements are less expensive to create.

Let’s look at React elements and their resulting JavaScript objects.

//<div>
//  <p>This is a list</p>
//  <ul>
//    <li>List item 1</li>
//    <li>List item 2</li>
//  </ul>
//</div>
 {
  "type": "div",
  "key": null,
  "ref": null,
  "props": {
    "children": [
      {
        "type": "p",
        "key": null,
        "ref": null,
        "props": {
          "children": "This is a list"
        },
        "_owner": null
      },
      {
        "type": "ul",
        "key": null,
        "ref": null,
        "props": {
          "children": [
            {
              "type": "li",
              "props": {
                "children": "List item 1"
              },
              // truncated for brevity
            },
            {
              "type": "li",
              "props": {
                "children": "List item 2"
              },
              // truncated for brevity
            }
          ]
        },
        "_owner": null
      }
    ]
  },
  "_owner": null
}

There are several properties here. Let’s go over some of them:

  • $$typeof identifies the object as a React element and is also used to improve React’s resilience against malicious markup.
  • key is used to uniquely identify elements among siblings while mapping over an array.
  • ref is a reference to an actual DOM node. It allows you to get direct access to a DOM element or an instance of a component.
  • props can be null or an object containing properties that are passed to the component.
  • type tells React what type of HTML element to create. This can be a string (div, h1), a React component (class or function), or a React fragment.
  • children are what you want to be passed into that element. When adding multiple children, we use an array and can nest as many children as we want.

On the other hand, React components encapsulate element trees, and components can either be classes or functions. Irrespective of their type, components take props as inputs and return an element tree as output. If it is a functional component, the output is the function’s return value, and if it is a class component, the output is the return value of the render method.

React keeps track of components by creating an instance for them. Each instance has a state and a lifecycle. In class components, we can access the state and the lifecycle using the predefined lifecycle methods and the this keyword, while in functional components we use React hooks.

React’s Rendering Process

Rendering is React’s process of describing a user interface based on the application’s current state and props. The initial render in a React app is the first render when the application starts up, while re-rendering occurs when there is a change in the state to figure out which parts of the UI need an update.

The rendering process can be divided into the Render and Commit phases.

Below is a visual description of the rendering process for initial render.

Image showing React rendering process

Below is a visual description of the rendering process when an application re-renders.

Image showing React re-rendering process

In the following sections, we’ll go over each of the phases as well as some of the underlying principles.

The Render Phase

Before React components can be displayed on the screen, they must first be rendered by React. The render phase is the initial phase of the rendering process. In this phase, the JSX code is converted to a JavaScript representation of what the HTML structure should look like.

On the initial render, it starts from the root component and works its way down, building out a React element tree of what the actual DOM should look like.

Re-rendering follows a similar approach but with a key difference: it creates a new JavaScript representation of the DOM, identifying all the components marked as needing an update. After this, it uses a process known as diffing to identify the changes between the old and new tree of React elements (aka the virtual DOM).

The Virtual DOM

Invoking a component causes React to instantiate it and return a plain JavaScript object describing a component or an HTML tag called a React element. This object and its children make up the virtual DOM.

React needs to ensure that the application’s UI is always in sync with the React state. To achieve this, whenever a component’s state changes, it has to re-render all potentially affected components (the component that owns the state and its descendants).

Writing to the actual DOM is very expensive and can lead to performance shortcomings, but generating the plain JavaScript objects (virtual DOM) is really easy. React uses the virtual DOM to reduce the excess cost incurred from repainting the entire page whenever a state change occurs that affects the UI.

The virtual DOM is used to figure out the changes without involving the actual DOM and then ensures that the actual DOM updates only the parts of the UI that have changed.

Reconciliation

Reconciliation is a process that occurs between the two phases of rendering. It covers how the virtual DOM gets synced with the real DOM.

On initial render, the code written in our components gets translated into React elements and mounted onto the DOM. However, React quickly generates a new tree of elements when an update occurs.

Next, it has to sync the virtual DOM containing the new tree with the actual DOM. It will be inefficient to re-render the actual DOM, as the process is quite costly, so React compares the trees and finds the least number of operations to transform one tree into another using what is known as the Diffing Algorithm. It tries to differentiate the trees to update only to the affected nodes in the real DOM.

The diffing algorithm is used behind the scenes to figure out how to efficiently update the UI to match the most recent tree. React implements a heuristic O(n) algorithm that is incredibly fast and possible because of the assumption that two elements of different types will produce different trees and that we provide a unique key as a prop to a list of child elements that change often. You can visit the official documentation to learn more about React’s diffing algorithm.

The Commit Phase

The actual DOM manipulation of the real DOM happens during the commit phase. The appendChild() DOM API is used for the initial render to put all the DOM nodes created on the screen. For re-renders, the minimal necessary operations (calculated while rendering) are applied to make the DOM match the latest rendering output.

Note that the React library does not communicate with the real DOM. It uses third-party packages known as Renderers like React DOM (for web platforms) and React Native (for mobile platforms) to handle the actual DOM manipulation. React gives us the means of expression so we can define components and so on.

In React web applications, we usually only import the React DOM package once, most times in the index.js file, and then call its render method. React communicates with the renderer using hooks or some other component lifecycle methods.

Tips to Optimize Render Performance

One of the techniques to optimize re-rendering in React applications is to adopt memoization in React components using hooks like the useCallback() or useMemo(). Memoization is an optimization technique that caches a component-rendered operation, saves the result in memory, and then returns the cached result for the same input.

We can also optimize rendering in React applications by keeping the component state local where necessary. A state update in a component causes the component that owns the state and its descendants to be re-rendered.

Conclusion

In this article, we looked at React components and elements, some important concepts about rendering, and the phases of the rendering process.


Ifeoma-Imoh
About the Author

Ifeoma Imoh

Ifeoma Imoh is a software developer and technical writer who is in love with all things JavaScript. Find her on Twitter or YouTube.

Related Posts

Comments

Comments are disabled in preview mode.