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

如何在Remix路由中嵌入带Loader的可复用路由组件?

针对Remix嵌入路由与复用组件的解决方案

一、将Apicheck路由内容嵌入Dashboard并正常加载

Remix中直接引入组件不会触发对应路由的Loader,你可以通过两种方式解决:

方案1:复用Loader逻辑,手动传递数据

把Apicheck的核心验证与数据获取逻辑抽成独立函数,在Dashboard的Loader中调用并将数据传给组件,同时保留Apicheck独立路由的功能:

  1. 抽取共享逻辑
// 可放在utils/apicheck.ts或直接在apicheck.tsx中导出
export async function getApicheckData({ request }: LoaderFunctionArgs) {
  // 身份验证逻辑
  const user = await authenticator.isAuthenticated(request, {
    failureRedirect: "/login",
  });
  // 数据获取逻辑
  const data = await fetchApicheckRelatedData(user);
  return { data, user };
}
  1. Apicheck路由使用共享逻辑
import { LoaderFunctionArgs, useLoaderData } from "@remix-run/react";
import { getApicheckData } from "./utils/apicheck";

export const loader = async ({ request }: LoaderFunctionArgs) => {
  return getApicheckData({ request });
};

// 支持props传入数据,兼容独立访问和嵌入场景
type ApicheckProps = {
  data?: typeof loader.returnType["data"];
};

export default function Apicheck({ data }: ApicheckProps) {
  const loaderData = useLoaderData<typeof loader>();
  const finalData = data ?? loaderData.data;
  
  return <div>{finalData.content}</div>;
}
  1. Dashboard路由调用共享逻辑并传递数据
import { LoaderFunctionArgs, useLoaderData } from "@remix-run/react";
import { getApicheckData } from "./utils/apicheck";
import Apicheck from "./apicheck";

export const loader = async ({ request }: LoaderFunctionArgs) => {
  // Dashboard自身的身份验证
  const user = await authenticator.isAuthenticated(request, {
    failureRedirect: "/login",
  });
  // 调用Apicheck逻辑获取数据
  const apicheckData = await getApicheckData({ request });
  return { user, apicheckData };
};

export default function Dashboard() {
  const { apicheckData } = useLoaderData<typeof loader>();
  
  return (
    <div>
      <h1>Dashboard</h1>
      <Apicheck data={apicheckData.data} />
    </div>
  );
}

方案2:使用嵌套路由(适合独立访问+嵌入展示场景)

将Apicheck设为Dashboard的子路由,既保留独立访问路径,又能在Dashboard中通过Outlet展示内容:

  1. 调整路由结构
routes/
├── dashboard.tsx
└── dashboard/
    └── apicheck.tsx
  1. Dashboard父路由添加Outlet
import { Outlet, useLoaderData } from "@remix-run/react";
import { LoaderFunctionArgs } from "@remix-run/node";

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const user = await authenticator.isAuthenticated(request, {
    failureRedirect: "/login",
  });
  return { user };
};

export default function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      {/* 子路由内容会渲染在这里 */}
      <Outlet />
    </div>
  );
}
  1. Dashboard子路由保留原逻辑
import { LoaderFunctionArgs, useLoaderData } from "@remix-run/react";

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const user = await authenticator.isAuthenticated(request, {
    failureRedirect: "/login",
  });
  const data = await fetchApicheckRelatedData(user);
  return { data };
};

export default function Apicheck() {
  const { data } = useLoaderData<typeof loader>();
  return <div>{data.content}</div>;
}

此时访问/dashboard/apicheck会同时加载Dashboard和Apicheck内容,若想在/dashboard默认展示Apicheck,可添加dashboard/index.tsx索引路由,在其中渲染Apicheck组件或重定向到/dashboard/apicheck。

二、实现布局中可复用的语言选择器

利用Remix布局的Loader加载全局数据,让导航栏中的语言选择器可以访问身份信息和后端数据:

  1. 根布局添加Loader和Action
import { LoaderFunctionArgs, ActionFunctionArgs, Outlet, useLoaderData } from "@remix-run/react";
import LanguageSelector from "./components/LanguageSelector";

export const loader = async ({ request }: LoaderFunctionArgs) => {
  // 全局身份验证(也可在子路由单独处理)
  const user = await authenticator.isAuthenticated(request, {
    failureRedirect: "/login",
  });
  // 获取语言列表数据
  const languages = await fetchSupportedLanguages();
  return { user, languages };
};

// 处理语言切换的Action
export const action = async ({ request }: ActionFunctionArgs) => {
  const formData = await request.formData();
  const language = formData.get("language") as string;
  const userId = formData.get("userId") as string;
  
  await updateUserPreferredLanguage(userId, language);
  return { success: true };
};

export default function Root() {
  const { user, languages } = useLoaderData<typeof loader>();
  
  return (
    <html>
      <head>...</head>
      <body>
        <nav>
          <LanguageSelector languages={languages} user={user} />
        </nav>
        <Outlet />
      </body>
    </html>
  );
}
  1. 语言选择器组件实现
import { Form } from "@remix-run/react";

type LanguageSelectorProps = {
  languages: Array<{ code: string; name: string }>;
  user: { id: string; preferredLanguage: string };
};

export default function LanguageSelector({ languages, user }: LanguageSelectorProps) {
  return (
    <Form method="POST">
      <input type="hidden" name="userId" value={user.id} />
      <select
        name="language"
        value={user.preferredLanguage}
        onChange={(e) => e.target.form?.submit()}
      >
        {languages.map(lang => (
          <option key={lang.code} value={lang.code}>
            {lang.name}
          </option>
        ))}
      </select>
    </Form>
  );
}

内容的提问来源于stack exchange,提问作者czioutas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 23:06:17