0

Given that a React component is essentially a function:

const Component = ([props]) => React.createElement(type[, props [, ...children]]);

when I try to call it as any normal function:

Component([props]); 

it doesn't work.

Meanwhile, in the React documentation it is called like this:

React.createElement(Component([props]));

How can a component, be used as a type property? Because normally, a type is a string, not a function.

  • related: https://stackoverflow.com/questions/69432301/react-hooks-call-component-as-function-vs-render-as-element – Giorgi Moniava Dec 12 '21 at 10:18
  • *"when I try to call it as any normal function:...it works as expected."* Could you define "works" there? It won't result in anything being rendered to the DOM, and if the function uses hooks, it will throw an error. – T.J. Crowder Dec 12 '21 at 10:33

1 Answers1

1

when I try to call it as any normal function:...it works as expected.

It may not throw an error (though it would if you used hooks), but it won't work correctly in the general case. (It might sort of work if your function is stateless and returns the result of calling createElement.)

React doesn't call your component function when you pass it to createElement, it just creates and returns a React Element object that stores the function along with the element's props and children:

const Example = ({value}) => {
    // (Returning a simple string to keep the example simple)
    console.log("Example called");
    return `Hi there, value = ${value}`;
};

console.log("Creating element (Example is never called)");
const element = React.createElement(Example, {value: "x"});
console.log("Element object:");
console.dir(element);
.as-console-wrapper {
    max-height: 100% !important;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

React calls the function later, if you use that element (and never if you don't use it). Usually it's more than once, because most elements are rendered more than once, and the function is caled for each render (unless you memoize it and the memo function says the result would be the same as last time). So the function may be called never (if the element is never used), or repeatedly (if the element is used and ever re-rendered).

Separately, React sets up some internal things at its end before calling your function, so that if your function uses hooks, it knows where to store the hook information. If you call a function that uses hooks directly, the hooks throw an error because that context isn't set up.

Here's a simple example (using createElement directly rather than via JSX, since your question mentioned createElement specifically):

const { useState, createElement } = React;

const Example = ({value}) => {
    const [counter, setCounter] = useState(0);
    
    console.log(`Example called with value = ${value}`);
    return createElement(
        "div",
        {value},
        [
            `Counter = ${counter}`,
            createElement(
                "input",
                {
                    key: "input",
                    type: "button",
                    value: "+",
                    onClick: () => setCounter(c => c + 1),
                }
            )
        ]
    );
};

console.log("Creating element without using it (function is never called):");
const result = createElement(Example, {value: "not used"});

console.log("Creating element and using it (function is called for each re-render):");
const App = () => {
    return createElement(Example, {value: "used"});
};

ReactDOM.render(
    createElement(App),
    document.getElementById("root")
);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • For some reason I thought that calling it as a normal function works. After verifying it again now, I realize that my question was wrong. – jurgen_gjoncari Dec 12 '21 at 11:16
  • @logicalclimber - You aren't the only one to think the function is called immediately, it's a **very** common misunderstanding (certainly one I had in my early days of using React). :-) – T.J. Crowder Dec 12 '21 at 11:21