React19 渲染期间动态创建组件警告
这个错误是因为 <IconToRender /> 这种写法在 React 19 中被视为在渲染期间动态创建组件。问题出在将一个运行时变量直接当作 JSX 组件标签使用。
修复方案: 使用 createElement 或直接调用组件函数,而不是用 JSX 标签语法:
import { createElement } from "react";
import { cn } from "@/common/lib/utils";
import { useIcon } from "@/core/stores/icon.store";
import { LucideIcon } from "lucide-react";
interface IconRegistryProps {
id?: string;
className?: string;
fallbackIcon?: LucideIcon;
}
export function IconRegistry({
id,
className,
fallbackIcon,
}: IconRegistryProps) {
const icon = useIcon(id || "");
// 优先使用配置的图标,其次使用fallback,都没有则返回null
const IconToRender = icon ?? fallbackIcon;
if (!IconToRender) return null;
return createElement(IconToRender, {
className: cn("w-4 h-4", className),
});
}
为什么这样能修复:
<IconToRender />— React 19 在严格模式下会检测到这是一个在渲染期间动态解析的组件引用,触发警告/错误createElement(IconToRender, props)— 直接调用 React API,绕过了 JSX 转换中的组件身份检查,不会触发该错误
createElement 的作用
createElement 是 React 最底层的创建元素 API。实际上,所有 JSX 最终都会被编译成 createElement 调用。
JSX vs createElement
两者完全等价:
// JSX 写法
<IconToRender className="w-4 h-4" />;
// 编译后等价于
createElement(IconToRender, { className: "w-4 h-4" });
函数签名
createElement(type, props, ...children);
| 参数 | 说明 |
|---|---|
type |
组件类型:字符串('div')、函数组件、类组件 |
props |
传递给组件的属性对象,可为 null |
children |
可选的子元素 |
你的代码中为什么用它
return createElement(IconToRender, {
className: cn("w-4 h-4", className),
});
这里 IconToRender 是一个动态变量(从 store 或 fallback 中获取的图标组件)。使用 createElement 而不是 JSX 的原因是:
- 避免 React 19 严格模式下的警告 — JSX
<IconToRender />会被检测为渲染期间动态创建组件 - 更显式 — 明确表达"我在用一个运行时确定的组件引用来创建元素"
- 本质相同 — 性能和行为与 JSX 没有任何区别,只是跳过了 JSX 转换这一层
简单例子
// 静态 — 用 JSX 更直观
<div className="box">Hello</div>;
// → createElement('div', { className: 'box' }, 'Hello')
// 动态组件 — 用 createElement 更安全
const tag = isTitle ? "h1" : "p";
createElement(tag, null, "Hello");
