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-domcreates 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;
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 ofDocumentseparated from the rest of the DOM
And on a one special comparison.
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.
<!-- react-mount-point-unstable -->
isModernRoot
isModernRoot validates whether the container wasn't previously passed to createRoot function. This check detects:
- absence of
_reactRootContainerproperty. This property indicates, that element was passed to therenderfunction. - presence of
__reactContainer$qk4eqywqqseproperty. This property indicates, that element was passed to thecreateRootfunction.$qk4eqywqqse- is a unique hash, generated on every newReactDOMinstance
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);
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!" />);
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
containera suitable element? - Was the
containerpreviously passed toReactDOM.createRootfunction?
Follow me on Twitter for daily bite-sized content like this! Let's become better engineers together!
Top comments (1)