An Introduction To Rendering In React

Uzair Shamim
Information & Technology
7 min readDec 30, 2017

In this post I want to talk about how React renders components, and how it tries to improve performance by using its reconciliation algorithm to only update the parts of the DOM that need updating. This is not meant to be an introduction to React, but I will quickly go over some of the relevant foundation concepts in the next section.

What Is React?

React is an open source JavaScript library created by Facebook to address some of the needs they had when dealing with the development of the Facebook website. React has played an important role in the evolution of JavaScript Frameworks because it is responsible for popularizing the Component Based Architecture (CBA) paradigm. An important distinction between React and frameworks like Angular is that React is only a library. For example, React does not provide its own routing or HTTP libraries. To perform these type of tasks in React developers have to rely on other libraries like Redux and the Fetch API.

Since React is simply a library, it can be used in existing projects very easily:

React using pure JavaScript (react docs)

While it is nice to be able to use React with pure JavaScript things can get hairy when writing a very large app. For this reason you may find it easier to use JSX, an alternative way of writing React code. JSX essentially allows you to combine HTML and JavaScript syntax together to make it easier to mange the code. The same example above can be written using JSX (more on it below):

React using JSX (react docs)

Component Based Architecture (CBA)

In this article I am not going to go into detail about CBA (there are a ton of great articles already available about that) but I will just explain some of the motivation as to why Reacts approach is useful for developers. When writing code using a framework like AngularJS, you have to use Controllers and Views to create your application. Controllers house the logic (JS) for your application while the Views house the UI elements (HTML). This separation of logic from UI allows for one to be changed without significantly impacting the other. For example if you need to change the logic in a function, this can be done without having to rewrite any of the UI. One of the problems with this approach however, is that it makes it difficult to modify a specific part of the UI without also having to modify other parts of the interface and logic.

What makes CBA different is that in CBA the logic and UI are kept together, causing the two to be coupled to each other. The goal of CBA is to encapsulate portions of an interface into self contained units called components. The use of encapsulation allows a component to be changed without the developer having to modify any other component. Another major benefit of components is that they are reusable, which is key to writing good code in React.

JSX

JSX works as a syntax extension for JavaScript that combines templating languages with JavaScript. While JSX is optional to use in React, it is much more elegant than using pure JavaScript so I prefer using it when building React applications. Just like XML, JSX elements can have names, attributes and children. Values enclosed in curly braces are interpreted as JavaScript expressions which you can use to substitute a hard coded value with a variable or evaluation.

JSX Example (via buildwithreact.com)

When React renders JSX (more on this later) it will convert the JSX elements into JavaScript and then use them to create the DOM. An interesting property of JSX is that it prevents injection attacks by converting everything into a string before rendering it. This ensures that a malicious user cannot execute XSS attacks using your application.

Unidirectional Data Flow

Another fundamental concept in React is the Unidirectional Data Flow which is how React propagates updates to components. Actions in the UI lead to the state of components being updated which in turn will cause the view to update to reflect these new changes.

Parent components can pass variables to their child components using props to provide them with the necessary context in which to render. Note that the arrows are not bidirectional, this is because components cannot update their parents, they can only receive updates from them.

The Initial Render

All React applications start at a root DOM node that marks the portion of the DOM that will be managed by React. You add child components to this node using React to get your application to look and behave the way you desire.

JSX

When React is called to render the component tree it will first need the JSX in your code to be converted into pure JavaScript. This can be achieved by making use of Babel. You may already be familiar with the Babel project for its transpiling capabilities, but it is also be used to convert the JSX you write into pure JavaScript. To see this in action you can check out this live demo on the Babel website. On the left is the JSX and on the right is the resulting JavaScript.

Original JSX
React after Babel

Lifecycle Methods

React comes with some lifecycle methods that you can use to update and control the application state. You can find out more about how and when to use them by reading this great article by Bartosz Szczeciński.

By default, React will re-render all child components of any component that itself has to re-render. This behavior may not always be ideal, for example when re-rendering the component requires performing costly calculations. The shouldComponentUpdate lifecycle method can be used to address this concern. By default it will always return true but you can add logic so that it only returns true under specific conditions. Note that this lifecycle method does not apply to when a component has its internal state changed using setState() which will still cause a re-render even if the shouldComponentUpdate method always returns false.

Updating Components

Once React has completed the initial render for your application it waits for one of two events before triggering an update for a component. Either the internal state of the component changes, or the props being passed into the component change.

Internal state of a component can be changed by calling the setState() function. The important thing to remember about setState() is that it is not executed immediately, as React may delay the state update.

Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.

— React Docs

Props, unlike state, cannot be modified by the component they are being passed into. Components must instead rely on their parent to provide updates to the props. These updates can occur under two situations, either the parent will update the props due to some internal state change or the child will call a function passed down by the parent that updates some state in the parent which in turn will update the props of the component causing it to re-render. Below is an example of how a child component would call a function passed down by its parent, note that it assumes component C did not need to be re-rendered (this is not always the case).

Example of child component (B) calling a function in its parent component (A). Green signifies the components that re-render.

Reconciliation

When React has to perform updates to a component it attempts to improve performance by only updating what needs to be updated. The way React accomplishes this is by using its ‘diffing’ or reconciliation algorithm. According to the React docs, there are two assumptions that React makes when it comes to reconciliation:

  • Two elements of different types will always produce different trees
  • Developers can hint which child elements are stable across different renders using a key prop

The reconciliation algorithm behaves differently depending on what type of root element it has to re-render.

Elements Of Different Types

If the new element is a different type of element than the old element (ex. changing from <span> to <h1>) then React has to perform a full rebuild of the tree. This means that it will destroy the old tree and build a new one from scratch. Note that since the old tree is destroyed, any state associated with it will also be gone. When the old nodes are destroyed, the componentWillUnmount lifecycle method will be invoked. As the new tree is created, the new nodes will call the componentWillMount and componentDidMount lifecycle methods.

Elements Of The Same Type

If the new element is the same type as the old element then React will keep the same DOM node and instead only updates the attributes that have changed. When the props are changed to match the new element, the componentWillReceiveProps and the componentWillUpdate lifecycle methods are called. For example if you update the style for an element, React knows to only update the specific properties of the CSS that changed. Note that since the node is not destroyed any state associated with the old node is still available. Once React has updated the node, it will recurse on its children.

Recursing On Children

When the reconciliation algorithm recurses on the children of a node, React iterates over a list of both the old and new children. There is a good explanation of how this works in the React docs. As mentioned in the docs, there may be performance issues if the new child is not appended to the list as React will not know what did and did not change. To avoid this issue you can use the key prop to give the element a unique ID so React can determine what actually changed.

Conclusion

Hopefully that was a useful explanation of how React does rendering of components and why its important to be aware of how you can improve its performance by following certain coding practices. If you have any questions or feedback, please leave a message in the comments.

--

--