ReactDOM.createRoot 🆚 ReactDOM.render
Tags: javascript, react, webdev, programming
Published: 2022-02-01 09:56:22

What is ReactDOM.createRoot and how is it different from ReactDOM.render?

React 18 introduces a new root API, so let's figure it out

👉 Current API  

We have to pass the `container` to render function every time we want to explicitly render.

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

👉 What does `ReactDOM.render` take?

`render` function takes three arguments:
- React element to be rendered
- DOM element to render in
- function to be executed after render happens

And returns the same `container`, but with the rendered component.

```js
/**
* @param element - React element to be rendered
* @param container - DOM element to render in
* @param callback - function to be executed after render happens
* @return container - container with renderned component
*/
function render(element, container, callback) {
  // ...
}
```

👉 How does `ReactDOM.render` work under the hood?

`ReactDOM.render` does a couple of validation checks:
- whether the container is a suitable node
- whether the container wasn't previously passed to `createRoot`

Then it passes all received arguments to `legacyRenderSubtreeIntoContainer`.

```js
// simplified structure
function render(element, container, callback) {
  if (isValidContainer(element)) {
    throw Error('Target container is not a DOM element.');
  }
  
  if (isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined) {
    // don't throw an error, but logs it into console
    error('container was previously passed to ReactDOM.createRoot().');
  }
  
  return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
}
```

👉 New API  

It fixes the issue of passing the container every time we want to explicitly render.

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

👉 What does `ReactDOM.createRoot` take?

`createRoot` function takes only one mandatory argument - DOM element to render in.
And returns `RootType`, which has render and unmount methods.

P.S. Also `createRoot` takes the second `RootOptions` argument, but we'll examine it in the future.

```js
/**
* @param container - DOM element to render in
* @param options - options, related to hydration
* @return RootType - instance of root
*/
function createRoot(container, options) {
  // ...
}
```

👉 How does `ReactDOM.createRoot` work under the hood?

On top of the render function, `createRoot`:
- checks whether the container isn't a body element
- provide a bit more detailed warnings

Then `createRoot` instance a new `ReactDOMRoot` object and returns it. No `legacyRenderSubtreeIntoContainer` at all!

```js
// simplified structure
function createRoot(container, options) {
  if (isValidContainer(element)) {
    throw Error('Target container is not a DOM element.');
  }
  
  if (container.nodeType === 1 && container.tagName.toUpperCase() === 'BODY') {
    console.error('Creating roots directly with document.body is discouraged');
  }
  
  if (isContainerMarkedAsRoot(container) {
    if (container._reactRootContainer) {
      console.error('container was previously passed to ReactDOM.render().')
    } else {
      console.error('container has already been passed to createRoot() before.');
    }
  }
  
  return new ReactDOMRoot(container, options);
}
```

Now you know the underline difference between old render and new `createRoot` functions!

Let me know, if you want a further comparison of `legacyRenderSubtreeIntoContainer` and `new ReactDOMRoot`.

P.S. [Follow me on Twitter](https://twitter.com/fromaline) for more content like this!

{% twitter 1486029718407335936 %}