React VDOM is lazy— the tale of the two render()-s : ReactDOM.render() vs Component.render()

Let’s look at this code : https://jsfiddle.net/je6bvfnk/

It outputs this to the screen :

and this to the console :

This shows that

  • the render() defined in a component (in the rest of the article it is called Component.render() ) completes for a given component (say for A1 — the program prints __root/A1 end )
  • then Component.render() starts for the child components (such as A1/B1)
  • but before Component.render() for the grandchildren starts ( ______root/A1/B1/C1 start ) the Component.render() for A1/B1 has finished and returned (____root/A1/B1 end).

This suggests that there are two types of render() at work, conceptually: React.render() and Component.render()

So the point is that Component.render() itself never calls another Component.render() directly. React.render() first calls Component.render() for A1 and then looks at A1 ‘s children and then calls ReactDOM.render() (or some conceptually similar internal function — similar in the sense that the VDOM output of `Component.render()` can be passed to ReactDOM.render() and that produces the corresponding DOM) for each of them.

This means that the VDOM returned by Component.render() is not the whole tree. But rather a lazy node. Let’s take for example the result of Component.render(A1/B2/C2/D1) (from this snippet) :

The type of the third child is a function. The corresponding part of the code is:

with the chId being 2 :

So the object produced by h(childCmpConstr, {parentsName: myName, chId: 2}) has a type field that is a function (h() is an alias for createElement()). So the VDOM returned by h does not go any deeper. It has a single node and it’s children however the children are not fully evaluated.

What does it mean to evaluate a child ?

In order to answer that, here we can look at all VDOM’s (returned by each componen’s Component.render() ):

and try to evaluate them ourselves and see what happens.

Let’s take for example A1/B1:

A1/B1' s second child is A1/B1/C2 but we cannot see C anywhere in the VDOM returned by A1/B1’s render() if we look at it by printing vdoms[“root/A1/B1”].props.children[2] to the console (see pic. above), so where is the information stored that the second child is a C type of component ?

It is stored in the `type` field as it can be seen by running this fiddle, which evaluates the second child :

console.log('passing vdoms["root/A1/B1" second child to ReactDOM.render()')
var element=h(vdoms["root/A1/B1"].props.children[2].type, {parentsName:'bla',chId:1})
ReactDOM.render(element, document.getElementById('react-app-tmp'))
  • Here we create element by passing vdoms[“root/A1/B1”].props.children[2].type to h (which is an alias for createElement ).
  • Then we pass element to ReactDOM.render() which causes element to be rendered to react-app-tmp <div>, and we get this printed to the screen (at the location of react-app-tmp <div>):

so this confirms that element corresponds to a C type of component (because that is what we see on the screen above) and it’s chId is 1 , it’s parent is bla .

So what is then vdoms[“root/A1/B1”] , the object returned by the root/A1/B1 component’s render() method ? It looks like a piece of VDOM. But how can we be sure? Let’s see what happens if we try to pass this object to ReactDOM.render() ?

var vdomA1B2=vdoms["root/A1/B1"]
var element=h(vdomA1B2.props.children[2].type, {parentsName:'bla',chId:1})
ReactDOM.render(element, document.getElementById('react-app-tmp'))
console.log('XXXXXXXXXX passing vdoms["root/A1/B1]" to ReactDOM.render()')
ReactDOM.render(vdomA1B2, document.getElementById('react-app-tmp-2'))

this gives:

so it seems that what a component's render() returns is a piece of `vdom` and it can be passed to ReactDOM.render()

So what is then vdomA1B2.props.children[2].type ? My guess is that it is a component class, the same kind of animal that is returned by createClass or for example when evaluating mkTreeComp(‘E’, ‘________’, true) in this code. The .type field is the field that contains what kind of component the current piece of VDOM is. The mkTreeComp(‘E’, ‘________’, true) creates an E type of component’s class. React instantiates this class and uses the `render()` method of that instance to create a piece of lazy VDOM node (as mentioned above).

Summary

There are two types of renders:

  • ReactDOM.render(vdom, dom-node) (this eats a vdom, and a dom-node), it generates a piece of dom from the vdom which will be mounted to the dom-node given in the second parameter
  • component.render() is render() method that exists on a component’s instance and is called by ReactDOM.render()

VDOM is a piece of js object that

  • is returned by a component’s render()
  • can be accepted by ReactDOM.render()
  • can be created by createElement()
  • it contains a list of children as VDOM objects, the type field of these vdom objects can be used to create instances of the child components whose render() method can be used to create the VDOM for the child components
  • it contains a type field which contains either a string (for a simple, not component VDOM, such as div) or a function/class that can be/is used by ReactDOM.render() to create a component whose render method could be used (but is pointless to do so) to create the very same VDOM that we are currently talking about.

Closing note: I am aware that this post needs some formatting improvements. Let’s consider this post version one. Any comments (about content) are welcome.