Next.js15中通过按钮动态渲染React组件并传参的实现方案
React动态渲染侧边面板并传递Props的类型解决方案
问题背景
我实现了一套包含主导航栏(Navbar)、导航链接(NavLinks)和侧边面板的组件结构,面板在Navbar内部渲染。原代码可正常运行,但鼠标悬停ActivePanel时出现类型警告:
Type 'ComponentType<{}>' is not assignable to type 'ReactNode'.
同时需要将从next-auth/react的useSession获取的userID作为props传递给激活的侧边面板。
核心需求
- 修复动态渲染组件的类型不匹配警告
- 向激活的侧边面板传递
userID参数
解决步骤
1. 修正Navbar的状态类型定义
更新useState的泛型约束,明确存储的组件必须接收userID: string类型的props,从根源上对齐类型:
const [ActivePanel, setActivePanel] = useState<React.ComponentType<{userID: string;}> | null>(null);
2. 同步NavLinks的Props类型
调整NavLinks的Props接口,确保setActivePanel的dispatch类型与Navbar中的状态类型完全一致:
interface Props { setActivePanel: React.Dispatch<React.SetStateAction<React.ComponentType<{ userID: string}> | null>>; }
点击逻辑可保持不变(或使用函数式更新进一步确保类型安全):
onClick={() => setActivePanel(item.panelComponent)}
3. 渲染组件实例并传递Props
这是解决类型警告的关键操作:原代码中直接渲染组件类型{ActivePanel}会触发类型不兼容,必须改为渲染组件实例并传入userID:
{ActivePanel && ( <div className="absolute top-0 left-0 w-full h-screen bg-amber-100/50 backdrop-blur-[2px]"> <div className="fixed top-0 right-0 min-w-[350px] min-h-screen flex flex-col gap-2 p-5 bg-amber-200 shadow-sm"> <button onClick={() => setActivePanel(null)} className="mb-2 text-sm text-gray-500" > close </button> {/* 改为组件实例并传递userID */} <ActivePanel userID={userID} /> </div> </div> )}
4. 确保面板组件接收Props
所有侧边面板组件(如PanelOne)需定义对应Props接口,接收userID参数:
// PanelOne.tsx interface Props { userID: string; } const PanelOne = ({ userID }: Props) => { return <div>Panel One - User ID: {userID}</div>; }; export default PanelOne;
最终完整代码示例
Navbar.tsx
import { useState } from "react"; import NavLinks from "./NavLinks"; const Navbar = () => { // 实际项目中从next-auth的useSession获取 const userID = "some-userID"; const [ActivePanel, setActivePanel] = useState<React.ComponentType<{userID: string;}> | null>(null); return ( <> <header className="sticky top-0 flex items-center justify-between p-10 "> <p>Logo Component</p> <NavLinks setActivePanel={setActivePanel} /> </header> {ActivePanel && ( <div className="absolute top-0 left-0 w-full h-screen bg-amber-100/50 backdrop-blur-[2px]"> <div className="fixed top-0 right-0 min-w-[350px] min-h-screen flex flex-col gap-2 p-5 bg-amber-200 shadow-sm"> <button onClick={() => setActivePanel(null)} className="mb-2 text-sm text-gray-500" > close </button> <ActivePanel userID={userID} /> </div> </div> )} </> ); }; export default Navbar;
NavLinks.tsx
import { NAV_ITEMS } from "./NAV_ITEMS"; interface Props { setActivePanel: React.Dispatch<React.SetStateAction<React.ComponentType<{ userID: string}> | null>>; } const NavLinks = ({ setActivePanel }: Props) => { return ( <div className="flex items-center gap-5"> {NAV_ITEMS.map((item) => ( <button key={item.text} onClick={() => setActivePanel(item.panelComponent)} className="cursor-pointer flex items-center gap-2 p-2" > {item.text} </button> ))} </div> ); }; export default NavLinks;
NAV_ITEMS.ts
import PanelOne from "@/components/sidePanels/PanelOne"; import PanelTwo from "@/components/sidePanels/PanelTwo"; import PanelThree from "@/components/sidePanels/PanelThree"; export const NAV_ITEMS = [ { text: "Panel One", panelComponent: PanelOne, }, { text: "Panel Two", panelComponent: PanelTwo, }, { text: "Panel Three", panelComponent: PanelThree, } ];
内容的提问来源于stack exchange,提问作者Andres Romero
相关产品推荐
相关产品推荐

