useLoaderData
5 min read
Table of Contents
核心概念
-
Loader (数据加载器):
- 在路由级别定义的异步函数。
- 在组件渲染之前执行。
- 用于获取组件所需的数据(API 请求、数据库查询等)。
- 命名规则:
- SSR 模式或服务端运行: 使用
loader。 - SPA 模式 (ssr: false) 或仅客户端运行: 使用
clientLoader。
- SSR 模式或服务端运行: 使用
-
useLoaderData (数据获取 Hook):
- 在组件内部使用的 Hook。
- 用于获取对应 Loader 函数返回的数据。
- 提供完整的类型推断支持。
基本用法
1. 定义 Loader 函数
在你的路由组件文件中(通常是页面级组件),导出名为 loader (SSR) 或 clientLoader (SPA) 的异步函数。
SPA 模式 (ssr: false) 示例:
import type { LoaderFunctionArgs } from "react-router";
// 注意:在 SPA 模式下必须命名为 clientLoaderexport 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 泛型。
import { useLoaderData } from "react-router";// 引入上面定义的 clientLoader 类型用于推断 (SPA 模式)// 如果是 SSR 模式,则使用 typeof loaderimport 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,包含 params 和 request。
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 中直接 throw 或 return 一个 redirect。
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 组件,而不是正常的页面组件。
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>;}重要提示
- 阻塞渲染: 默认情况下,React Router 会等待 Loader 执行完毕后才渲染组件。如果 Loader 很慢,页面切换会有明显的延迟。建议在父级布局中使用
useNavigation显示全局 Loading 状态。 - SSR vs SPA:
- SPA 模式 (
ssr: false): 必须使用export function clientLoader。它始终在浏览器中运行。 - SSR 模式 (
ssr: true): 使用export function loader(服务端运行) 和可选的export function clientLoader(客户端运行)。
- SPA 模式 (
- 序列化: 返回的数据必须是可序列化的(JSON 兼容),不能传递函数或类实例(除非使用特定的库如
superjson,但这通常不建议)。
总结
useLoaderData 模式消除了传统的 useEffect + useState + fetch 的样板代码,解决了“渲染后获取”导致的布局抖动(Waterfall)问题,是现代 React Router 开发的核心实践。
评论 (0)
请先登录后再发表评论