useLoaderData

5 min read
Table of Contents

核心概念

  1. Loader (数据加载器):

    • 在路由级别定义的异步函数
    • 在组件渲染之前执行。
    • 用于获取组件所需的数据(API 请求、数据库查询等)。
    • 命名规则:
      • SSR 模式或服务端运行: 使用 loader
      • SPA 模式 (ssr: false) 或仅客户端运行: 使用 clientLoader
  2. useLoaderData (数据获取 Hook):

    • 在组件内部使用的 Hook。
    • 用于获取对应 Loader 函数返回的数据。
    • 提供完整的类型推断支持。

基本用法

1. 定义 Loader 函数

在你的路由组件文件中(通常是页面级组件),导出名为 loader (SSR) 或 clientLoader (SPA) 的异步函数。

SPA 模式 (ssr: false) 示例:

tsxapp/routes/blog-post.tsx
import type { LoaderFunctionArgs } from "react-router";
// 注意:在 SPA 模式下必须命名为 clientLoader
export async function clientLoader({ params, request }: LoaderFunctionArgs) {
// 1. 获取 URL 参数
const postId = params.postId;
// 2. 发起数据请求 (可以是 fetch, 数据库查询等)
const response = await fetch(`https://api.example.com/posts/${postId}`);
if (!response.ok) {
throw new Response("Not Found", { status: 404 });
}
const post = await response.json();
// 3. 返回数据
return { post };
}

2. 在组件中使用数据

在同一个文件中,使用 useLoaderData 获取数据。为了获得类型提示,可以使用 typeof clientLoader 泛型。

typescript
import { useLoaderData } from "react-router";
// 引入上面定义的 clientLoader 类型用于推断 (SPA 模式)
// 如果是 SSR 模式,则使用 typeof loader
import type { clientLoader } from "./blog-post";
export default function BlogPost() {
// data 将自动推断为 { post: ... } 类型
const data = useLoaderData<typeof clientLoader>();
return (
<div>
<h1>{data.post.title}</h1>
<p>{data.post.content}</p>
</div>
);
}

进阶场景

1. 获取 URL 参数和查询参数 (Query Params)

Loader 函数接收一个参数对象 args,包含 paramsrequest

typescript
export async function clientLoader({ request, params }: LoaderFunctionArgs) {
// 获取动态路由参数,例如 /users/:userId
const userId = params.userId;
// 获取 URL 查询参数,例如 /search?q=react
const url = new URL(request.url);
const searchTerm = url.searchParams.get("q");
return { userId, searchTerm };
}

2. 处理重定向

如果你需要根据逻辑重定向用户(例如未登录用户跳转到登录页),可以在 Loader 中直接 throwreturn 一个 redirect

typescript
import { redirect } from "react-router";
export async function clientLoader() {
const user = await getUser();
if (!user) {
// 中断加载并跳转
throw redirect("/login");
}
return { user };
}

3. 错误处理

如果 Loader 执行过程中抛出错误(throw new Error(...)throw new Response(...)),React Router 会捕获它并渲染最近的 ErrorBoundary 组件,而不是正常的页面组件。

typescript
export async function clientLoader() {
const res = await fetch("/api/data");
if (res.status === 404) {
throw new Response("Not Found", { status: 404 });
}
return res.json();
}
// 对应的错误边界组件
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
return <div>出错了</div>;
}

重要提示

  1. 阻塞渲染: 默认情况下,React Router 会等待 Loader 执行完毕后才渲染组件。如果 Loader 很慢,页面切换会有明显的延迟。建议在父级布局中使用 useNavigation 显示全局 Loading 状态。
  2. SSR vs SPA:
    • SPA 模式 (ssr: false): 必须使用 export function clientLoader。它始终在浏览器中运行。
    • SSR 模式 (ssr: true): 使用 export function loader (服务端运行) 和可选的 export function clientLoader (客户端运行)。
  3. 序列化: 返回的数据必须是可序列化的(JSON 兼容),不能传递函数或类实例(除非使用特定的库如 superjson,但这通常不建议)。

总结

useLoaderData 模式消除了传统的 useEffect + useState + fetch 的样板代码,解决了“渲染后获取”导致的布局抖动(Waterfall)问题,是现代 React Router 开发的核心实践。

后记

My avatar

感谢你读到这里。

这座小站更像一份持续维护的“终端笔记”:记录我解决问题的过程,也记录走过的弯路。

如果这篇内容对你有一点点帮助:

  • 点个赞 / 收藏一下,方便你下次回来继续翻
  • 欢迎在评论区补充你的做法(或者指出我的疏漏)
  • 想持续收到更新:可以订阅 RSS(在页面底部)

我们下篇见。


More Posts

评论

评论 (0)

请先登录后再发表评论

加载中...