You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+TypeScript中Portal容器类型不匹配求非any解决方案

React + TypeScript Portal类型不匹配问题解决方案

问题描述

在React结合TypeScript开发时,使用自定义use-portal钩子创建了Element类型的容器元素,但调用createPortal时出现类型不匹配错误——React要求传入ReactNode类型参数,而当前传入的是Element。除了用any类型,有什么可行的解决方案?

报错信息

Argument of type 'Element' is not assignable to parameter of type 'ReactNode'.
  Type 'Element' is missing the following properties from type 'ReactPortal': key, type, props

相关代码

Portal.tsx

import { createPortal } from "react-dom";

import { usePortal } from "./use-portal";

interface PortalProps {
  rootId: string;
  children: JSX.Element;
}

const Portal: React.FC<PortalProps> = ({ rootId, children }) => {
  const portalContainer = usePortal(rootId);
  return createPortal(portalContainer, children);
};

export default Portal;

use-portal.tsx

const createRootElement = (id: string): Element => {
  const rootContainer = document.createElement("div");
  rootContainer.setAttribute("id", id);
  return rootContainer;
};

const addRootElement = (rootElem: Element): void => {
  const elements = document.querySelectorAll("body > div");
  if (elements.length > 0) {
    const lastElement = elements[elements.length - 1];
    document.body.insertBefore(rootElem, lastElement);
  } else {
    document.body.append(rootElem);
  }
};

export const usePortal = (id: string): Element => {
  const existingElement = document.getElementById(id);
  const portalElement = existingElement ?? createRootElement(id);
  if (!existingElement) {
    addRootElement(portalElement);
  }
  return portalElement;
};

解决方案

核心问题是**createPortal的参数顺序传反了**:createPortal第一个参数是要渲染的ReactNode(即children),第二个参数才是挂载目标DOM元素(portalContainer)。参数顺序错误直接导致类型不匹配。

具体修复步骤

  1. 修正createPortal参数顺序
    在Portal.tsx中,将createPortal(portalContainer, children)改为createPortal(children, portalContainer)。

  2. 优化类型定义(可选,提升类型安全性)

    • 把PortalProps的children类型从JSX.Element改为React.ReactNode,支持更广泛的子元素类型(如字符串、多个元素集合等)。
    • 将use-portal中相关类型从泛型Element改为更精确的HTMLElement,因为我们创建的是div元素,符合实际场景。

修复后的代码如下:

修复后的Portal.tsx

import { createPortal } from "react-dom";
import { usePortal } from "./use-portal";

interface PortalProps {
  rootId: string;
  children: React.ReactNode; // 扩展支持更多子元素类型
}

const Portal: React.FC<PortalProps> = ({ rootId, children }) => {
  const portalContainer = usePortal(rootId);
  return createPortal(children, portalContainer); // 修正参数顺序
};

export default Portal;

修复后的use-portal.tsx

const createRootElement = (id: string): HTMLElement => {
  const rootContainer = document.createElement("div");
  rootContainer.setAttribute("id", id);
  return rootContainer;
};

const addRootElement = (rootElem: HTMLElement): void => {
  const elements = document.querySelectorAll("body > div");
  if (elements.length > 0) {
    const lastElement = elements[elements.length - 1];
    document.body.insertBefore(rootElem, lastElement);
  } else {
    document.body.append(rootElem);
  }
};

export const usePortal = (id: string): HTMLElement => {
  const existingElement = document.getElementById(id) as HTMLElement | null; // 类型断言为HTMLElement
  const portalElement = existingElement ?? createRootElement(id);
  if (!existingElement) {
    addRootElement(portalElement);
  }
  return portalElement;
};

修改后类型错误会完全消除,同时不需要使用any类型,代码的类型安全性也得到提升。

内容的提问来源于stack exchange,提问作者Pentium3k

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 08:01:43