Deep dive into React codebase [EP4: How ReactDOM.render works]
Tags: javascript, webdev, react, programming
Published: 2022-01-30 08:01:42

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](https://dev.to/fromaline/deep-dive-into-react-codebase-ep3-reverse-engineer-the-most-famous-react-snippet-njj) 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](https://github.com/fromaline/deep-dive-into-react-codebase/blob/master/episodes/ep4/source-code-snippet.js), 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**.
```js
container.nodeType === 1;
```
`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.**
```js
node.nodeType === 8 && node.nodeValue === ' react-mount-point-unstable '
```
This check detects whether the provided container is a comment with `react-mount-point-unstable` text. 
```html
<!-- react-mount-point-unstable -->
```
[It's undocumented and unstable feature, that allows to render React components without extra wrapper, like `<div id="root"></div>`](https://twitter.com/fromaline/status/1487059240468590596). 

#### `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
```js
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);
```

New API
```js
// 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!" />);
```

#### `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](https://twitter.com/fromaline) for daily bite-sized content like this! Let's become better engineers together!