为啥 React 组件是通过 `React.createElement()` 方法执行的?

Posted

技术标签:

【中文标题】为啥 React 组件是通过 `React.createElement()` 方法执行的?【英文标题】:Why are React components executed through the `React.createElement()` method?为什么 React 组件是通过 `React.createElement()` 方法执行的? 【发布时间】:2022-01-16 05:02:37 【问题描述】:

鉴于 React 组件本质上是一个函数:

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

当我尝试将其作为任何正常功能调用时:

Component([props]); 

它不起作用。

同时,在 React 文档中它是这样调用的:

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

如何将组件用作type 属性?因为通常情况下,typestring,而不是函数。

【问题讨论】:

相关:***.com/questions/69432301/… “当我尝试将它作为任何普通函数调用时:...它按预期工作。” 你能在那里定义“工作”吗?它不会导致任何东西被渲染到 DOM,如果函数使用钩子,它会抛出错误。 【参考方案1】:

当我尝试将它作为任何普通函数调用时:...它按预期工作。

它可能不会抛出错误(尽管如果你使用了钩子会抛出错误),但在一般情况下它不会正常工作。 (如果您的函数是无状态的并返回调用createElement 的结果,它可能会起作用。)

当你将它传递给createElement 时,React 不会调用你的组件函数,它只是创建并返回一个 React Element 对象,该对象存储该函数以及元素的 props 和子元素:

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 调用函数稍后,如果你使用那个元素(如果你不使用它永远不会)。通常不止一次,因为大多数元素被多次渲染,并且每次渲染都会调用函数(除非你memoize 它并且备忘录函数说结果与上次相同)。所以这个函数可能永远不会被调用(如果元素从未被使用过),也可能会被重复调用(如果元素被使用过并且曾经重新渲染过)。

另外,React 在调用你的函数之前会在它的末尾设置一些内部的东西,这样如果你的函数使用钩子,它就知道在哪里存储钩子信息。如果你调用一个直接使用钩子的函数,钩子会抛出一个错误,因为没有设置上下文。

这是一个简单的示例(直接使用 createElement 而不是通过 JSX,因为您的问题特别提到了 createElement):

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>

【讨论】:

出于某种原因,我认为将其作为普通函数调用是可行的。现在再次验证后,我意识到我的问题是错误的。 @logicalclimber - 你不是唯一一个认为该函数被立即调用的人,这是一个非常常见的误解(当然是我在早期使用 React 时遇到的一个误解)。 :-)

以上是关于为啥 React 组件是通过 `React.createElement()` 方法执行的?的主要内容,如果未能解决你的问题,请参考以下文章