Join us on the demo, while our product experts provide a detailed walkthrough of our enterprise platform.
Get LoginRadius Swags in Hacktoberfest 2020. Check our hacktoberfest page for more details.

Understanding React Rendering

Learn to optimize React rendering process.

Nathan Nguyen
Nathan Nguyen
September 23, 2020
8 min read

Rendering is the most important procedure that a programmer has to manage in frontend development. In React, the render() method is the only required method in a class component, and is responsible for describing the view to be rendered to the browser window. Coupled with the clever way that React operates around its virtual DOM concept, there are certain subtleties in how this method works, and understanding them will greatly benefit any aspiring React developer.

Throughout this writing, I will reference this codepen for a demonstration of discussed behaviors.

1. render() 101

First of all, render() is not user callable. It is part of the React component lifecycle and is called by React at various app stages, generally when the React component is first instantiated, or when there is a new update to the component state. Render does not take any arguments, and returns a JSX.Element which contains the view hierarchy of the current component. This view hierarchy will later be translated into HTML and displayed in the browser window.

As mentioned before, render() is not user callable as it is an event that happens in the component’s lifecycle. With that said, if it is absolutely necessary to manually render the view, you can instead call the built-in class method forceUpdate(). Keep in mind that this is considered an anti-pattern. If you were designing sensible React components, its state and props changes should naturally control the render process, and you should never feel the need to make a manual call.

Within the lifecycle, these are the scenarios where render is called:

  • After the React component is first instantiated, following the constructor() call.
  • After an update to the component’s props
  • After a setState() call

If you have the Codepen opened at this point, before anything is rendered you will see 2 alert messages from the browser: "render() is called in Parent component!", and "render() is called in Child component!". These messages are invoked from the corresponding render() methods of the example’s parent and child component. They serve to introduce the first case of render() invocation: when the component is first instantiated.

Once the set of alerts is dismissed, a very simple UI will render:

Example UI

The dotted border line serves to distinguish between elements that belong to the Child component of the example (inside the dotted line), versus the Parent component.

  • Clicking button 1 will update the childElementText state of the Parent component which in turns updates the text prop of the Child component, triggering a render in both Parent and Child.
  onChildPropChange = () => {
    this.setState({
      childElementText: "I am the child element! I am updated following a prop change."
    })
  }
  • Clicking button 2 will update the helloWorldMessage state within Child component, triggering a render following a state change.
  onTextChange = () => {
    this.setState({
      helloWorldMessage: "Hello React! (state change after setState call)"
    })
  }

A visual and interactive reference to the React lifecycle can be found here.

It is worth noting that following a props update or setState(), the method shouldComponentUpdate() is invoked to determine whether render() should be called. By default, this method always returns true, but it can be overloaded to implement custom logic. This is the main way to define custom render behavior in each React component.

shouldComponentUpdate() provides you with nextProp and nextState as arguments, which allows you to compare the current state and props of the component. For example, this code block will invoke render() only when the text prop changes:

  shouldComponentUpdate(nextProps: NewComponentProps, nextState: NewComponentState) {
    if (this.props.text !== nextProps.text) {
      return true;
    } else {
      return false;
    }
  }

The characteristics and behaviors mentioned above made it imperative that render() is a pure function. This means inside render(), you should not make an update to the component’s states or props (no setState() call nor Redux state update). This makes sense because an update to the component will then trigger a new render() call, which can potentially lock you into an infinite render loop.

In terms of return value: render() returns a single JSX element, as mentioned above. This comes with certain implications:

  • If you need to return a collection of sibling elements, you need to wrap them all in a parent
    , or a <React.Fragment>. It is worth noting that once rendered, <React.Fragment> will vanish from the DOM structure. It is only meant to be a wrapper component and does not appear in the final DOM in the browser. This makes it a more sensible choice over a
    wrapping to avoid nesting div’s.
  • JSX is immutable. The returned JSX element is a constant that represents the state of the DOM to be rendered. Therefore, when thinking about how to write a render() method, it is helpful to think about how the entire UI will look like at a moment in time, instead of thinking about how a certain element updates over time. “Thinking about how the UI should look at any given moment, rather than how to change it over time, eliminates a whole class of bugs.”

2. Notes on reconciliation, and the key prop

The render() method in each React component then feeds into what is called the Reconciliation Algorithm. This is the primary algorithm that dictates how React renders the real DOM in your browser based on a virtual DOM maintained internally by React. To intelligently determine what needs to be rendered on every call, React compares the current state of the virtual DOM and the real one and only makes changes to the physical DOM where it recognizes that the UI has been updated.

While not every single detail is known about React’s reconciliation algorithm, the characteristics detailed in the official documentation are enough for us to start phasing out certain suboptimal rendering patterns, thus writing a more robust render() method.

  • On a UI update: As the DOM tree is parsed top to bottom, if a mismatch of elements is detected, React will tear down and rebuild the entire subtree starting from that element. If the subtree is complex, this operation can be quite costly. Therefore, if a new element were to be introduced to the DOM tree, it should be appended as the last element in that level if there are no specific requirements of where it should be placed.

For instance, given this DOM subtree:

<div>
  <span key="li-1">list item 1</span>

  <span key="li-2">list item 2</span>

  <span key="li-3">list item 3</span>
</div>

If a is then added to the top of the list:

<div>
  <!-- previously <span>list item 1</span> - element is detached and <NewComponent /> instantiated -->
  <NewComponent />

  <!-- previously <span>list item 2</span> - content will be updated to "list item 1"  -->
  <span>list item 1</span>

  <!-- previously <span>list item 3</span> - content will be updated to "list item 2"  -->
  <span>list item 2</span>

  <!-- new <span>list item 3</span> is element created  -->
  <span>list item 3</span>
</div>

If instead is added to the bottom:

<div>
  <!-- previously <span>list item 1</span> - no change -->
  <span>list item 1</span>

  <!-- previously <span>list item 2</span> - no change -->
  <span>list item 2</span>

  <!-- previously <span>list item 3</span> - no change -->
  <span>list item 3</span>

  <!-- new instance of <NewComponent /> is added -->
  <NewComponent />
</div>

In the example above, appending to the end of the list will result in 1 new instantiation, versus 1 instantiation, 3 updates and 1 tear down. In a larger application scale, this will prove to be a significant performance difference in the long run.

  • If you have ever tried to use the map() method to iterate over an array to render a list of elements, it is likely that you have seen React complaining about a missing key prop to each rendered list item. So what does key actually do?

    A key is React’s way to recognize elements in the DOM tree, comes reconciliation time. When React is parsing all children of an element, it can leverage keys to match elements that were present from the last update. This allows you to shuffle the order of child elements without interfering with the algorithm. As long as the key matches between updates, React will preserve the element configuration.

Coming back to the example above, let’s add keys to all the existing list items:

<div>
  <span key="li-1">list item 1</span>

  <span key="li-2">list item 2</span>

  <span key="li-3">list item 3</span>
</div>

In this case, if we were to add an extra “list item 4” at the top of the list, it would not come with the same performance penalty:

<div>
  <!-- new item - <span> is instantiated -->
  <span key="li-4">list item 4</span>

  <!-- matched with the old "li-1" key - element stays unchanged between renders -->
  <span key="li-1">list item 1</span>

  <!-- matched with the old "li-2" key - element stays unchanged between renders -->
  <span key="li-2">list item 2</span>

  <!-- matched with the old "li-3" key - element stays unchanged between renders -->
  <span key="li-3">list item 3</span>
</div>

In case a subtree is generated using a map() or other iterative methods, React requires that keys are provided with the element. However, even in case a DOM subtree is manually added, keys should be provided on subtrees that have complex behaviors regarding conditional rendering.

3. Parting words

React is a clever framework that offers performance through its rendering scheme, so it follows that, as developers, we should leverage it in a proper manner to help build performant applications. With that said, it is not a miracle device that optimizes out inefficiencies from the developer’s end, but a tool to be utilized. Understanding the render() method as well as its implication to the reconciliation algorithm is the very first step to making sure we are leveraging the framework instead of working against it, as well as one of the first steps to mastering React.



LoginRadius Docs

Implement Authentication in Minutes

click here

Most Popular Tags

EngineeringJavaScriptNodeJSCSSOAuthSecuritySocialLoginGoReactAuthentication

Are your customers safe on your application?

According to Forbes, data breaches exposed 4.1 billion records in the first six months of 2019. If this gets you worried, we have got your back!

LoginRadius protects your customers’ identities. We provide world class security for your customers during login, registration, password setup, and any other data touchpoints, and make sure that their data is safe. In parallel, it offers open source SDKs, integrations with over 150 third party applications, pre-designed and customizable login interfaces, and best-in-class data security products such as MFA, RBA, Advanced Password Policies. The platform is already loved by over 3,000 businesses with a monthly reach of 1.17 billion users worldwide.Secure Your Application Now

Nathan Nguyen

Nathan Nguyen

Nathan Nguyen is a Software Engineer at LoginRadius. With deep interest in creating user-facing applications, he spends a lot of his time with web and mobile technology stacks, off and on the job, looking to build applications that are ever more intuitive and helpful to end users. Loves food, travel, photography and video games.

View Profile

Try a Modern Authentication Solution

$0/ month

Free Sign Up
  • 5,000 MAU
  • 1 Web or mobile app
  • Standard login
  • 3 Social Login Providers
  • Transactional Email Template
  • Customizable Login Interfaces