Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

During a render, why wrap a functional component in a React.createElement instead of utlizing its returned element via a function call?

const App = () => {
 return React.createElement(
   "div",
   {},
   React.createElement('h1', {}, "Adopt The Pet"))
 }
}

ReactDOM.render(App(), document.getElementById("root")); // this works

// vs

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

I am following an introduction to react course in which the author uses the second version with a new React.createElement() wrapper. I can't figure out the need to do so even after going through the relevant react docs.

The first version already returns a React.createElement() so why not use that returned element instead of wrapping it inside a new React.createElement()?

Edit: Even the React documentation uses the course author's method if you translate the doc's JSX to React.createElement() calls but again, they haven't explained why.

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// React.createElement(Welcome, {name : Sara})
const element = <Welcome name="Sara" />; 

// Why not just do this?
const element =  Welcome({Sara})

ReactDOM.render(
  element,
  document.getElementById('root')
);

1 Answers

This has to do with what a "React component" actually is. According to the React documentation:

Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen. - https://reactjs.org/docs/components-and-props.html

Boiling that down I understand a React component to be "a function that returns a React element" (ignoring classes altogether for now).

function component() {
  return React.createElement('h1', null, 'Hello World')
}

So if that's the case, we should be able to pass our React component directly to ReactDOM.render() or React.createElement():

ReactDOM.render(component, document.getElementById("root"))
// or
React.createElement('div', null, component)

But we can't. This doesn't work. It gives us the following error:

Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.

Sure, we can fix this by invoking our function:

ReactDOM.render(component(), document.getElementById("root"))
// or
React.createElement('div', null, component())

But is that really a React component? No. It's just invoking a plain old function to ultimately get a React element as the return value. To illustrate this point further, look what happens when we try and use the useState hook:

function component() {
  const [state, setState] = React.useState(0);
  return React.createElement('h1', null, 'Hello World')
}

ReactDOM.render(component(), document.getElementById("root"))

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

But wait a minute, we thought we had a function React component?

A function that returns a React element does not become a "React component" until it is passed as the first argument to React.createElement().

This makes sense if we think about it. If you define a function, there's no way for React to know that your function is a React component. You pass the function as reference, but React doesn't know that what you're passing will ultimately return a React element (per our definition). We need to somehow indicate to React that our function is indeed a "React component." We do this by passing. our function as the first argument to React.createElement()

// Regular old function
function component() {
  return React.createElement('h1', null, 'Hello World')
}

// becomes a React component
React.createElement(component)

This, among other things like the useState() hook above, is also required for React to take control of the props passed as arguments to our function, so it can re-render when our props change. We have to give React control of our function. So, to amend our previous definition of what a React element is, we land here:

A React component is "a function that returns a React element and is passed as the first argument to React.createElement()"

When we use JSX this is all abstracted away, so I assume that why people don't really talk about it.

like image 89
Galupuf Avatar answered Sep 11 '25 02:09

Galupuf