Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does ReactDOM.render need to call function or class components again after creating the virtual DOM?

I understand that React uses React.createElement() in recursive way to create the virtual DOM.

For example: if we have an App component which has this markup:

<div className="app">
  <span>3<span/>
  <Br />
  content 1
<div/>

Turns into:

 ReactDOM.render(React.createElement(App, {}))

and this returns this object:

{
  type: App, 
  props: {}, 
}

and this will call another React.createElement

React.createElement('div', { className: 'app"}, //children) 

and this will return this object:

{
  type: 'div',
  props: {
    className: 'app',
    children: [
      React.createElement('span', {}, 3), 
      React.createElement('br'),
      'content 1'
    ]
  }
}

and this process continues until the whole virtual DOM is created.

Now when ReactDOM.render() works it uses these objects to create the real DOM nodes in the browser.

I am reading this article that explains the process well but something is confusing to me here,

If a type attribute holds a string with a tag name—create a tag with all attributes listed under props.

If we have a function or a class under type—call it and repeat the process recursively on a result.

If there are any children under props—repeat the process for each child one by one and place results inside the parent’s DOM node.

My problem is here:

If we have a function or a class under type—call it and repeat the process recursively on a result

Why doesn't ReactDOM.render() just use the objects created by React.createElement() instead of calling the function components again to get the underlying DOM elements? Am I missing something? If React.createElement() returns all the details about the component why do we need to do that again??

like image 562
Code Eagle Avatar asked Sep 10 '26 12:09

Code Eagle


1 Answers

I think that part of the problem is that the information in the premise you've provided is oversimplified compared to the actual return value of React.createElement. Let's look at a real example with a parent component using an instance of the state hook, and a child component using its props (without any JSX, like in your post):

<div id="root"></div>

<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<script type="module">

const {createElement, useState} = React;

function Count (props) {
  return createElement('span', null, props.value);
}

function App () {
  const [count, setCount] = useState(0);

  const reactElement = createElement(
    'div',
    null,
    createElement(
      'div',
      null,
      createElement('span', null, 'Count:'),
      ' ',
      createElement(Count, {value: count}),
    ),
    createElement('button', {onClick: () => setCount(n => n + 1)}, 'Increment'),
  );

  console.log(reactElement);
  return reactElement;
}

ReactDOM.render(createElement(App), document.getElementById('root'));

</script>

Look at the JS console in your browser when running the example above, and examine the props object on the logged react element. Locate the children property on props, and examine each object in the array. Then repeat this process for every object in the array recursively, all the way down the node hierarchy.

After studying the values, click the "Increment" button in the example, and examine the structure again (recursively) after the rerender. You will find differences in the structure, and some of these differences are caused by the changes to the props object being passed to the function component Count. This simple example illustrates one reason why it is necessary to invoke each functional component in every render, however there are many more internal state values created and used by React that depend on the deterministic invocation of these functions on every render.

like image 81
jsejcksn Avatar answered Sep 12 '26 02:09

jsejcksn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!