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)。参数顺序错误直接导致类型不匹配。
具体修复步骤
修正
createPortal参数顺序
在Portal.tsx中,将createPortal(portalContainer, children)改为createPortal(children, portalContainer)。优化类型定义(可选,提升类型安全性)
- 把
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
相关产品推荐
相关产品推荐

