How React calculates the DOM
and making it more efficient

DOM Reconciliation

Memo

Pure Components

First, what is the DOM?

Document Object Model

It’s what’s really on the page

picture of the DOM in Firefox dev tools

React has its own kind of DOM

picture of the React dev tools

React takes JSX and creates its own Virtual DOM (VDOM)

This is the result of every component's render function

Component Tree

It then diffs its VDOM with the real DOM

The real DOM

Data tree 1

React's VDOM

Data tree 2

And makes the smallest amount of changes to the DOM so it matches the VDOM

This works really well, until…

1 We don't have as much CPU

Like on a phone

2 We have a lot of components

Therefore lots of render functions to calculate

How do we fix these problems?

By avoiding calculations

One option is to use Pure components

Which are essentially just pure functions

Input -> Output

Same input gives the same output, every time

Props -> JSX

Same Props render the same JSX, every time

If the input doesn't change, the output will always be the same


const MyComponent(props) => {
  return <a href={props.to}>{props.text}</a>
}

<MyComponent to="/about" text="About" />

// Output
<a href="/about">About</a>

        

We can avoid doing expensive calculations, if we save a copy of the output

Keyed by the input

This is called Memoization

Or as React likes to call it Memo

Functional components can do this by using React.Memo()


const MyComponent = React.memo(MyComponent(props) {
  /* render using props */
});
        

We avoid costly calculations by trading computation for memory

Kinda similar to a materialized view


const MyComponent = React.memo((props) => {
  return <a href={props.to}>{props.text}</a>
});

<MyComponent to="/about" text="About" />

// 1st time calls the inner function
<a href="/about">About</a>

// 2nd time return pre-saved output
<a href="/about">About</a>

        

If props are the same it will skip re-rendering itself and all child components

Component Tree

Warning! It only does a shallow diff

1 object layer deep


React.memo(Component, optionalComparisonFunc(prevProps, nextProps));

        

What other options do we have?

What about Class components?

Class components have lifecycle methods that are called automatically by React

React Lifecycle Method Diagram

shouldComponentUpdate
( nextProps, nextState ) -> true / false

Called just before a re-render

This allows you to decide if a component needs to update the VDOM

By returning true or false

If false it will skip re-rendering itself and all child components

React already does this for you with React.PureComponent

Replace React.Component with React.PureComponent


class MyComponent extends React.Component {
  shouldComponentUpdate( nextProps, nextState ) {
    return true;
  }

  render() {
    // Return JSX
  }
}

class MyComponent extends React.PureComponent {
  render() {
    // Return JSX
  }
}
          
        

You can skip huge chunks of calculations by being clever about when to re-render

Component Tree

Warning! It only does a shallow diff

1 object layer deep

How do you know which components are rendering a lot?

React Dev Tools is your friend!

picture of the React dev tools
Demo

That's how to make React more efficient, by skipping calcuations

In summary

React calculates its own VDOM every re-render

React then diffs its VDOM with the real DOM and makes the smallest amount of changes needed

VDOM calculations can be skipped to save on compute time

Memo can be used to skip re-calculation time for functional components

shouldComponentUpdate and React.PureComponent can be used to skip re-calculating class components

Use React Dev Tools to inspect and profile your components

The most efficient calculation is to skip the calculation

Or pre calculate if possible

Thank you, I hope this has been helpful

jasongorman.dev/slides/how-react-calculates-the-dom/