React Router路由加载时如何显示过渡加载页面?
解决React Router Loader加载耗时导致无反馈的问题
核心思路
放弃让路由loader阻塞导航的模式,改用defer异步加载+页面内Suspense兜底,实现即时跳转并在数据就绪前显示加载状态,直接解决你遇到的两个场景问题。
修复defer方案的正确用法
你之前尝试defer未生效,大概率是在loader中错误地await了异步请求,导致仍然阻塞导航。正确的做法是在loader中直接返回未解析的Promise,配合React Suspense实现页面内加载状态。
1. 修改路由配置
给路由添加id(方便后续获取loader数据),并使用defer返回异步请求的Promise:
import { createBrowserRouter, defer } from "react-router-dom"; import Dashboard from "./Dashboard"; // 你的异步请求函数 async function getData() { const res = await fetch("/api/dashboard"); if (!res.ok) throw new Error("数据加载失败"); return res.json(); } const router = createBrowserRouter([ { path: "/dashboard", element: <Dashboard />, id: "dashboard", // 添加路由ID loader: async () => { // 关键:不await getData,直接返回Promise return defer({ dashboardData: getData(), }); }, }, ]);
2. 改造Dashboard组件
用React Suspense包裹需要数据的渲染区域,设置加载状态的兜底UI,再通过useRouteLoaderData和React 18的use钩子获取异步数据:
import { Suspense } from "react"; import { useRouteLoaderData } from "react-router-dom"; import { use } from "react"; // 负责渲染数据的子组件 function DashboardContent() { const { dashboardData } = useRouteLoaderData("dashboard"); // 使用use钩子解析Promise const data = use(dashboardData); return ( <div className="dashboard-content"> {/* 这里渲染你的数据内容 */} <h1>欢迎来到仪表盘</h1> <p>用户数据:{JSON.stringify(data)}</p> </div> ); } // 主组件,处理加载状态 function Dashboard() { return ( <div className="dashboard-container"> {/* 即时显示加载状态,直到数据就绪 */} <Suspense fallback={<div className="loading">加载中...</div>}> <DashboardContent /> </Suspense> </div> ); } export default Dashboard;
针对两种场景的优化说明
- 场景1:点击跳转按钮无反馈:确保使用React Router的
Link组件(而非自定义的异步跳转逻辑),点击后会立即导航到/dashboard路由,页面会即时切换并显示Suspense的加载状态,不再停留在原页面等待数据。import { Link } from "react-router-dom"; // 跳转按钮示例 <Link to="/dashboard">进入仪表盘</Link> - 场景2:直接访问URL页面空白:访问
/dashboard时,路由会立即渲染Dashboard组件,Suspense的fallback会即时显示加载状态,直到数据加载完成后替换为实际内容,彻底解决空白等待问题。
替代方案:手动管理加载状态
如果你不想使用Suspense,也可以在loader中返回包含加载状态的对象,手动在组件中处理:
// 路由loader loader: async () => { return { dashboardData: getData(), // 返回Promise }; } // Dashboard组件 function Dashboard() { const { dashboardData } = useLoaderData(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { dashboardData .then(res => { setData(res); setLoading(false); }) .catch(err => { setError(err.message); setLoading(false); }); }, [dashboardData]); if (loading) return <div className="loading">加载中...</div>; if (error) return <div className="error">{error}</div>; return <div>{/* 渲染数据 */}</div>; }
这种方式更灵活,但代码量稍大,推荐优先使用Suspense+defer的方案,更符合React Router的设计理念。
内容的提问来源于stack exchange,提问作者Wamy-Dev
相关产品推荐
相关产品推荐

