一、前言
useLoaderData
,用于在组件中获取路由预加载的数据。它通常与路由配置中的 loader
函数配合使用,用于在页面渲染前异步获取数据(如 API 请求),并将数据直接注入组件,从而简化数据流管理。
二、useLoaderData核心用途
预加载页面数据:在路由匹配时自动触发数据加载,减少组件渲染后的等待时间。
简化组件逻辑:组件无需手动处理数据获取和状态管理。
支持服务端渲染 (SSR):与 React Router
的服务端渲染方案无缝集成。
数据共享:同一路由下的嵌套组件可直接访问 loader
数据。
三、useLoaderData基本使用步骤
定义路由配置:在路由中声明 loader
函数。
在组件中获取数据:通过 useLoaderData()
读取 loader
返回的数据。
四、示例:用户信息页面
4.1、 定义路由配置(使用 createBrowserRouter)
// src/main.jsx
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import UserPage, { userLoader } from "./UserPage";
const router = createBrowserRouter([
{
path: "/user/:userId",
element: <UserPage />,
loader: userLoader, // 数据预加载函数
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<RouterProvider router={router} />
);
4.2 定义 Loader 函数(异步获取数据)
// src/UserPage.jsx
export async function userLoader({ params }) {
// 从路由参数中获取 userId
const userId = params.userId;
try {
// 模拟 API 请求
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error("用户不存在");
const userData = await response.json();
return userData; // 返回的数据会被 useLoaderData() 接收
} catch (error) {
// 抛出错误,由 React Router 错误边界处理
throw new Response("加载用户数据失败", { status: 404 });
}
}
4.3、 在组件中使用 useLoaderData
// src/UserPage.jsx
import { useLoaderData } from "react-router-dom";
export default function UserPage() {
// 直接获取 loader 返回的数据
const user = useLoaderData();
return (
<div>
<h1>用户信息</h1>
<p>姓名:{user.name}</p>
<p>邮箱:{user.email}</p>
<img src={user.avatar} alt="用户头像" />
</div>
);
}
五、参数与返回值
参数:无。
返回值:由当前路由的 loader 函数返回的数据(类型不限)。
六、注意事项
6.1、必须与路由 loader 绑定
只有在路由配置中定义了 loader,useLoaderData
才能获取到数据。
6.2、数据作用域
数据与当前路由关联,切换路由时数据会自动更新。
6.3、类型安全
默认情况下,useLoaderData 返回 unknown 类型(TypeScript)
。建议通过类型断言或 loader
类型定义明确数据类型:
// TypeScript 示例
const user = useLoaderData() as UserType;
6.4、错误处理
若 loader 抛出错误(如 throw new Response()
),需通过 React Router
的错误边界处理。
示例:在路由配置中添加 errorElement:
{
path: "/user/:userId",
element: <UserPage />,
loader: userLoader,
errorElement: <ErrorPage />, // 自定义错误页面
}
6.5、依赖关系
loader
在以下情况重新执行:
路由参数变化(如从 /user/1 跳转到 /user/2)。
通过 useNavigation().formAction 或 useRevalidator()
手动触发重新验证。
七、高级用法:嵌套数据与重定向
7.1、 嵌套路由共享数据
// 父路由 loader
export async function parentLoader() {
return { appVersion: "1.0.0" };
}
// 子路由组件中可直接访问父级 loader 数据
function ChildComponent() {
const parentData = useLoaderData(); // 获取父路由的 loader 数据
return <div>版本:{parentData.appVersion}</div>;
}
7.2、 在 Loader 中重定向
export async function authLoader({ request }) {
const isLoggedIn = checkUserAuth();
// 未登录时重定向到登录页
if (!isLoggedIn) {
throw redirect("/login");
}
return fetchProtectedData();
}
八、完整案例:博客文章列表
8.1、 Loader 函数(获取文章列表)
// src/routes/postsLoader.js
export async function postsLoader() {
const response = await fetch("/api/posts");
const posts = await response.json();
return { posts };
}
8.2、路由配置
// src/main.jsx
const router = createBrowserRouter([
{
path: "/posts",
element: <PostsPage />,
loader: postsLoader,
errorElement: <ErrorPage />,
},
]);
8.3、 组件渲染
// src/PostsPage.jsx
import { useLoaderData } from "react-router-dom";
export default function PostsPage() {
const { posts } = useLoaderData();
return (
<div>
<h1>所有文章</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</li>
))}
</ul>
</div>
);
}
十、最佳实践
10.1、分离数据逻辑
将 loader
函数单独放在 routes/ 目录中
,保持组件专注于渲染。
10.2、缓存与优化
使用 shouldRevalidate
控制数据重新加载条件,避免不必要的请求。
10.3、加载状态提示
配合 useNavigation
显示加载动画:
function PostsPage() {
const { posts } = useLoaderData();
const navigation = useNavigation();
if (navigation.state === "loading") {
return <Spinner />;
}
return /* 渲染文章列表 */;
}
useLoaderData,可以 实现数据加载与 UI 渲染的分离,让代码更清晰、更易维护。尤其适合中大型应用中需要统一管理数据请求的场景。