Remix嵌套动态路由调用正确Loader却渲染父页面问题咨询
问题描述
访问路由/events/23/sections/38-service-2时,Remix成功调用了/events.$eventId.services.$serviceName.tsx中的loader函数,但页面却渲染了父文件/events.$eventId.tsx的默认组件,效果与访问/events/23完全一致。
嵌套路由文件 /events.$eventID.services.$serviceName.tsx 代码
import type { LoaderFunctionArgs } from "@remix-run/node"; import { json } from "@remix-run/node"; import { useLoaderData } from "@remix-run/react"; import invariant from "tiny-invariant"; import { requireUserId } from "~/session.server"; import { getTicketsByEventAndService } from "~/utils/events.server"; export const loader = async ({ params, request }: LoaderFunctionArgs) => { await requireUserId(request); invariant(params.eventId, "noteId not found"); console.log("Loader Details Service Page") const data = await getTicketsByEventAndService(Number(params.eventId), params.serviceName); //console.log("Loader Details Service Page: ",data) if (!data) { console.log("Service Detail Page Not found") throw new Response("Not Found", { status: 404 }); } return json(data); }; export default function ServiceDetailsPage() { const data = useLoaderData<typeof loader>(); console.log("Tickets: ", data); return ( <div> <h3 className="text-2xl font-bold"> Details Ticket page</h3> </div> ); }
父文件 /events.$eventID.tsx 代码
import type { LoaderFunctionArgs } from "@remix-run/node"; import { json} from "@remix-run/node"; import { Link, useLoaderData } from "@remix-run/react"; import invariant from "tiny-invariant"; import { requireUserId } from "~/session.server"; import { getTicketsByEventId } from "~/utils/events.server"; export const loader = async ({ params, request }: LoaderFunctionArgs) => { await requireUserId(request); invariant(params.eventId, "noteId not found"); console.log("EventsPage Loader Called") const data = await getTicketsByEventId(Number(params.eventId)); if (!data) { throw new Response("Not Found", { status: 404 }); } return json(data); }; export default function EventsPage() { const { tickets, inventory } = useLoaderData<typeof loader>(); console.log("EventsPage Rendered") return ( <div> <h3 className="text-2xl font-bold"> Total sold tickets: {tickets.length}</h3> <Link to={`services`}> Services Page</Link> {Object.keys(inventory).map((key) => { const event = inventory[key]; return <EventRow key={key} event={event} />; })} </div> ); } const EventRow = ({ event }) => { const totalServices = Object.keys(event).filter((key) => key.startsWith("service"), ).length; const services = []; for (let idx = 1; idx <= totalServices; idx++) { services.push(event[`service-${idx}`]); } return ( <div className="flex flex-col pt-10 divide-y divide-dashed hover:divide-solid"> <h3 className="text-xl"> {event.text} </h3> <div className="flex flex-row items-start"> {services.map((service) => ( <Link to={`services/${service.key}`} key={service.key} className="text-lg pl-5 pt-5 border-r-8 hover:bg-orange-300"> <p> {service.text}</p> <p> Sold: {service.sold}</p> </Link> ))} </div> </div> ); };
原因分析
路由路径与文件命名不匹配
你访问的路由是/events/23/sections/38-service-2,但嵌套路由文件命名为events.$eventID.services.$serviceName.tsx,该文件对应的路由是/events/:eventId/services/:serviceName。路径中的sections和文件命名里的services不对应,导致Remix无法匹配到子路由组件,但eventId参数能被正确提取,所以loader会执行,但子组件无法渲染,最终只显示父组件内容。父组件缺少
<Outlet />占位符
即便路由路径匹配正确,父组件EventsPage也没有引入Remix的<Outlet />组件。<Outlet />是渲染子路由组件的必要占位符,没有它的话,子路由的组件永远不会被渲染,页面只会显示父组件的内容。
内容的提问来源于stack exchange,提问作者Muntram van Chen
相关产品推荐
相关产品推荐

