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

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>
  );
};

原因分析

  1. 路由路径与文件命名不匹配
    你访问的路由是/events/23/sections/38-service-2,但嵌套路由文件命名为events.$eventID.services.$serviceName.tsx,该文件对应的路由是/events/:eventId/services/:serviceName。路径中的sections和文件命名里的services不对应,导致Remix无法匹配到子路由组件,但eventId参数能被正确提取,所以loader会执行,但子组件无法渲染,最终只显示父组件内容。

  2. 父组件缺少<Outlet />占位符
    即便路由路径匹配正确,父组件EventsPage也没有引入Remix的<Outlet />组件。<Outlet />是渲染子路由组件的必要占位符,没有它的话,子路由的组件永远不会被渲染,页面只会显示父组件的内容。

内容的提问来源于stack exchange,提问作者Muntram van Chen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 01:57:38