Next.js 14.0.3:服务端传图标到客户端组件报错求解决
问题描述
使用Next.js 14.0.3时,将Lucide图标从服务端组件传递到客户端组件时,出现错误:
Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
服务端组件代码(SidebarRoutes.tsx):
import React from "react"; import { Layout, Compass } from "lucide-react"; import SidebarItem from "./SidebarItem"; const guestRoutes = [ { label: "Home", path: "/", icon: Layout, }, { label: "Browse", path: "/search", icon: Compass, }, ]; function SidebarRoutes() { const routes = guestRoutes; return ( <div className="flex mt-4 flex-col h-full"> {routes.map((route) => { return ( <SidebarItem key={route.path} icon={route.icon} label={route.label} path={route.path} /> ); })} </div> ); } export default SidebarRoutes;
客户端组件代码(SidebarItem.tsx):
'use client'; import React from "react"; import { LucideIcon } from "lucide-react"; import { usePathname, useRouter } from "next/navigation"; interface SidebarItemProps { label: string; path: string; icon: LucideIcon; } const SidebarItem = ({ label, icon: Icon, path }: SidebarItemProps) => { const router = useRouter(); const pathname = usePathname(); return ( <Icon/> ); }; export default SidebarItem;
由于需要使用usePathname做条件渲染,SidebarItem必须是客户端组件,求修复方法。
修复方法
方法1:传递图标名称,客户端动态映射组件
修改服务端组件传递图标名称字符串,客户端通过映射表匹配对应图标组件:
服务端组件(SidebarRoutes.tsx)修改:
import React from "react"; import SidebarItem from "./SidebarItem"; const guestRoutes = [ { label: "Home", path: "/", iconName: "Layout", }, { label: "Browse", path: "/search", iconName: "Compass", }, ]; function SidebarRoutes() { const routes = guestRoutes; return ( <div className="flex mt-4 flex-col h-full"> {routes.map((route) => { return ( <SidebarItem key={route.path} iconName={route.iconName} label={route.label} path={route.path} /> ); })} </div> ); } export default SidebarRoutes;
客户端组件(SidebarItem.tsx)修改:
'use client'; import React from "react"; import { LucideIcon, Layout, Compass } from "lucide-react"; import { usePathname, useRouter } from "next/navigation"; // 建立图标名称与组件的映射关系 const iconMap: Record<string, LucideIcon> = { Layout, Compass, }; interface SidebarItemProps { label: string; path: string; iconName: keyof typeof iconMap; } const SidebarItem = ({ label, iconName, path }: SidebarItemProps) => { const router = useRouter(); const pathname = usePathname(); const Icon = iconMap[iconName]; return ( <Icon /> ); }; export default SidebarItem;
方法2:服务端预渲染图标JSX,传递渲染结果
直接在服务端组件中渲染图标,将已生成的JSX传递给客户端组件:
服务端组件(SidebarRoutes.tsx)修改:
import React from "react"; import { Layout, Compass } from "lucide-react"; import SidebarItem from "./SidebarItem"; const guestRoutes = [ { label: "Home", path: "/", icon: <Layout />, }, { label: "Browse", path: "/search", icon: <Compass />, }, ]; function SidebarRoutes() { const routes = guestRoutes; return ( <div className="flex mt-4 flex-col h-full"> {routes.map((route) => { return ( <SidebarItem key={route.path} icon={route.icon} label={route.label} path={route.path} /> ); })} </div> ); } export default SidebarRoutes;
客户端组件(SidebarItem.tsx)修改:
'use client'; import React from "react"; import { usePathname, useRouter } from "next/navigation"; interface SidebarItemProps { label: string; path: string; icon: React.ReactNode; } const SidebarItem = ({ label, icon, path }: SidebarItemProps) => { const router = useRouter(); const pathname = usePathname(); return ( <>{icon}</> ); }; export default SidebarItem;
方法3:创建服务端图标包装组件
通过服务端组件包装图标,再将包装后的JSX传递给客户端:
创建IconWrapper.tsx(服务端组件):
import React from "react"; import { LucideIcon } from "lucide-react"; interface IconWrapperProps { icon: LucideIcon; props?: React.ComponentProps<LucideIcon>; } export default function IconWrapper({ icon: Icon, props }: IconWrapperProps) { return <Icon {...props} />; }
服务端组件(SidebarRoutes.tsx)修改:
import React from "react"; import { Layout, Compass } from "lucide-react"; import SidebarItem from "./SidebarItem"; import IconWrapper from "./IconWrapper"; const guestRoutes = [ { label: "Home", path: "/", icon: <IconWrapper icon={Layout} />, }, { label: "Browse", path: "/search", icon: <IconWrapper icon={Compass} />, }, ]; function SidebarRoutes() { const routes = guestRoutes; return ( <div className="flex mt-4 flex-col h-full"> {routes.map((route) => { return ( <SidebarItem key={route.path} icon={route.icon} label={route.label} path={route.path} /> ); })} </div> ); } export default SidebarRoutes;
客户端组件(SidebarItem.tsx)同方法2的修改即可
内容的提问来源于stack exchange,提问作者user22741641
相关产品推荐
相关产品推荐

