Virtual DOM
The vDOM
is an in-memory JavaScript representation of the DOM
. Just as the DOM is built of nodes, the vDOM is built of virtual nodes (vNodes).
vNodes
can be broadly categorized into two types: virtual elements (vElement) or virtual text elements (vText). These virtual nodes can be mapped to respectively the DOM's HTMLElement
and TextNode
.
What does the virtual DOM look like
A vDOM simply refers to a JavaScript representation of the actual DOM. There is no strict rule on how the virtual DOM should look. For example, we can represent this DOM node...
<p class="emphasis">Hello, world!</p>
as (1) this
const vNode = {
tagName: "p",
attrs: {
className: "emphasis"
},
children: ["Hello, world!"]
}
or (2) this
const vNode = {
type: "p",
props: {
className: "emphasis"
},
descendents: ["Hello, world!"]
}
As long as we can find a reliable way to reconstruct the actual DOM using any version of its virtual representation, we are fine.
createElement(type, props, ...children)
Most virtual DOM implementation will have a function called createElement
like in React.createElement()
. Such a function simply returns a virtual element given some parameters describing what type of element we would like to create.
const createElement = (type, props = {}, ...children) => {
return {
type,
props,
children,
};
};
const vApp = createElement(
"div",
{ className: "app" },
createElement("p", { className: "emphasis" }, "Hello, world!")
);
If you log vApp
, it should look like this:
{
type: 'div',
props: { className: 'app' },
children: [
{
type: 'p',
props: { className: 'emphasis' },
children: [ 'Hello, world!' ]
}
]
}
vDOM to DOM
The vDOM is a plain JavaScript object, and we cannot magically insert these into the native DOM. The browser needs actual native nodes to display HTML. React uses a render
function that expects a vDOM node to be passed as a parameter and it will return a native DOM node that can be inserted or appended to the DOM.
DOM Refresher
The vDOM should have all the details you need to create an equivalent representation in the real DOM. In case you don't remember DOM related methods, I have included them below.
// Creating an element
const $el = document.createElement("h1");
// Setting non-style attributes
$el.className = "emphasis";
// Setting style attributes
$el.style.border = "1px solid black";
// Appending a child element
const textEl = document.createTextNode("Hello, world!");
$el.appendChild(textEl)
JSX
If you read official Babel JSX documentation, you’ll know that Babel transpiles this code:
<ul className="list">
<li>item 1</li>
<li>item 2</li>
</ul>
into this
React.createElement("ul", { className: "list" },
React.createElement("li", {}, "item 1"),
React.createElement("li", {}, "item 2"),
);
We've been doing a similar thing. Instead of React.createElement
, we just have createElement
. But wouldn't it just be wonderful if we can stop explicitly calling createElement
and just write JSX just as you would do in React! Yes, you actually can with a plugin called babel-plugin-transform-react-jsx
. Install the plugin using yarn or npm and we can now include a comment-like line called a JSX pragma at the top of our source file:
/** @jsx createElement */
<ul className=”list”>
<li>item 1</li>
<li>item 2</li>
</ul>
This is what will tell Babel to transpile the JSX using our createElement
function and not React.createElement
. After transpiling, we get the following:
createElement(
"ul",
{ className: "list" },
createElement(
"li",
{},
"item 1"
),
createElement(
"li",
{},
"item 2"
)
);
Rarely if ever will you find the need to create your own
createElement
function. This is strictly for your reference.
Capitalize your component name
When JSX is being transpiled, Babel uses the case of the tag name to determine if we are describing a component or an HTML element. This is why we always start component names with a capital letter.
Always import React from 'react'
Make sure to import React even if we don't see a reference to React anywhere in our code because remember: JSX ultimately gets converted to a bunch of React.createElement
. We need a reference to React.
// do this
import React from 'react';
Hopefully you enjoyed this whirlwind tour of concepts I wish someone had taught me when I was first learning React. Obviously, these details are unimportant to many of you who don't care about the implementation detail. At the very least, I hope you found these concepts informative!
PS: Sometimes it's fun to take a peak under the hood!
Warmly,
DH
Top comments (7)
This is one of those things I wish I knew before I cast aside vDOM as a black box sometime earlier in my career. There are some good practices here, great for anyone picking up React. Kudos
Wow, I didn't know this post would get nearly this much attention. Thank you to all those who checked it out. More content to come~
There are a lot of beginners here as well as people who need the occasional reminder of the basics so just keep on pushing out whatever you think is worth sharing :)
How does this help me? Could you give 3 examples where this has helped you write better react?
Hi, Stephen! It's true that knowing how an internal combustion engine works doesn't necessarily make you a better driver. But it does certainly help you understand why you should get an oil change or not put regular fuel in a premium-fuel only car.
As for me personally, I like knowing these details about React because they help me with debugging. Knowing that JSX is just a JavaScript object at the end of the day helps me understand that
diffing
in React-speak is simply the process of comparing two JavaScript objects. Now objects can have many things in them including other objects. When you compare two objects which are reference types, it can be quite tricky for JavaScript to figure out if they are "deeply" equal. This is why you would use immutable data structures when settingstate
. Otherwise, even if the contents of those objects have changed, React which uses a shallow equality check, would not deem two objects to be unequal.It is always good to know why you're doing whatever you're doing instead of blindly following a pattern and being like "ok this is magical but it works".
Good One.