DEV Community

Cover image for Deep dive into React codebase [EP4: How ReactDOM.render works]
Nick
Nick

Posted on

Deep dive into React codebase [EP4: How ReactDOM.render works]

In today's episode, we take a bite-sized approach to learning how ReactDOM.render works and what it does!

Quick recall

In the previous episode we figured out how React.createElement works. In short:

There are two functions:

  • createElement - for production use
  • createElementWithValidation - for dev purposes

createElementWithValidation uses createElement internally, but adds validation and meaningful warnings.
createElement returns a JS object with a bunch of properties. This object is a virtual DOM node.

For us the most important properties are:

  • type - defines type of the element
  • props - contains all props passed to the element
  • children - contains all children of the element

How ReactDOM.render works

Recalling the assumption

In the previous episode, we formed an assumption regarding how react-dom works as a whole. Here it is:

react-dom creates a real DOM structure, based on the virtual DOM, and mounts the result in the provided container.

Continue with testing the assumption

Our entry point is the render function, it takes three arguments.

  • element - React element to be rendered
  • container - DOM element to render in
  • callback - function to be executed after render happens

Inside render function ReactDOM does couple of checks.

isValidContainer

isValidContainer checks whether the container is a suitable node.
This check is based on a couple of comparisons, like this one.

container.nodeType === 1;
Enter fullscreen mode Exit fullscreen mode

nodeType is an integer, that indicates what the node is.
1 for Element node, 3 for Text node and so on.

To pass this check a container should be either:

  • Element - the most general class of all element objects
  • Document - the main object of the rendered DOM
  • DocumentFragment - lightweight version of Document separated from the rest of the DOM

And on a one special comparison.

node.nodeType === 8 && node.nodeValue === ' react-mount-point-unstable '
Enter fullscreen mode Exit fullscreen mode

This check detects whether the provided container is a comment with react-mount-point-unstable text.

<!-- react-mount-point-unstable -->
Enter fullscreen mode Exit fullscreen mode

It's undocumented and unstable feature, that allows to render React components without extra wrapper, like <div id="root"></div>.

isModernRoot

isModernRoot validates whether the container wasn't previously passed to createRoot function. This check detects:

  • absence of _reactRootContainer property. This property indicates, that element was passed to the render function.
  • presence of __reactContainer$qk4eqywqqse property. This property indicates, that element was passed to the createRoot function. $qk4eqywqqse - is a unique hash, generated on every new ReactDOM instance

createRoot is a new API introduced in React 18. It fixes the issue of passing the container every time we want to explicitly render.

Old API

const container = document.querySelector('#root');

// Initial render. Container is explicitly accessed.
ReactDOM.render(<App text="Hello" />, container);

// Subsequent renders. Container is explicitly accessed.
ReactDOM.render(<App text="Hello world!" />, container);
Enter fullscreen mode Exit fullscreen mode

New API

// First, we create a root
const root = ReactDOM.createRoot(document.querySelector('#root'));

// Initial render. Container is implicitly accessed.
root.render(<App name="Hello" />);

// Subsequent renders. Container is implicitly accessed.
root.render(<App name="Hello world!" />);
Enter fullscreen mode Exit fullscreen mode

legacyRenderSubtreeIntoContainer

After these two checks render passes element, container and callback to the legacyRenderSubtreeIntoContainer function and returns the result.

We'll examine what this function does and how it works in the next episode!

Wrap up

Today we learned

Today we learned, that ReactDOM.render function is basically a wrapper for legacyRenderSubtreeIntoContainer. But at the same time it checks a couple of crucial things:

  • Is passed container a suitable element?
  • Was the container previously passed to ReactDOM.createRoot function?

Follow me on Twitter for daily bite-sized content like this! Let's become better engineers together!

Top comments (1)

Collapse
 
c9der profile image
Amit Kumar

Hi,
I was actually looking for way to create a function that can replace React.createElement();

to Create Some React Element Like this.

const ExampleComponent=()=>{
const [state,setState] = React.useState(0);
function handleClick(e:any) {
e.preventDefault();
setState(state+1);
}
return (<>
<button onClick={handleClick}>
{state}</button>
</>);
}

Can I create Similar React Element Like ExampleComponent using React.createElement()?
If Yes {
How Can I create ExampleComponent Using React.createElement()?
}
I know I can User React.element(ExampleComponent,{},React.createElement('button',{onClick:handleClick},{state});

but for this I have to create state by hand no it is not pure React.
I want to create It without JSX.