Yasin

Yasin

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 的原因是:

  1. 避免 React 19 严格模式下的警告 — JSX <IconToRender /> 会被检测为渲染期间动态创建组件
  2. 更显式 — 明确表达"我在用一个运行时确定的组件引用来创建元素"
  3. 本质相同 — 性能和行为与 JSX 没有任何区别,只是跳过了 JSX 转换这一层

简单例子

// 静态 — 用 JSX 更直观
<div className="box">Hello</div>;
// → createElement('div', { className: 'box' }, 'Hello')

// 动态组件 — 用 createElement 更安全
const tag = isTitle ? "h1" : "p";
createElement(tag, null, "Hello");