为啥 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
属性?因为通常情况下,type
是 string
,而不是函数。
【问题讨论】:
相关:***.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 调用函数稍后,如果你使用那个元素(如果你不使用它永远不会)。通常不止一次,因为大多数元素被多次渲染,并且每次渲染都会调用函数(除非你memo
ize 它并且备忘录函数说结果与上次相同)。所以这个函数可能永远不会被调用(如果元素从未被使用过),也可能会被重复调用(如果元素被使用过并且曾经重新渲染过)。
另外,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()` 方法执行的?的主要内容,如果未能解决你的问题,请参考以下文章
ReactFire - 将 Firebase 绑定到 React 状态时,获取“对象作为 React 子项无效”
在渲染期间,为啥要在 React.createElement 中包装一个函数式组件,而不是通过函数调用来使用它返回的元素呢?