如何使用React Router创建响应式主详情应用?
响应式主详情布局问题
我准备了一个测试用例,动态效果如下:
在RootLayout.jsx中,通过媒体查询在窄屏(≤768px)时隐藏Outlet,拖动浏览器窗口缩小就能看到这个效果:
// RootLayout.jsx import { Outlet } from "react-router"; import { useMediaQuery } from "@react-hook/media-query"; import MasterList from "../components/MasterList"; export default function RootLayout() { const isSmallScreen = useMediaQuery("(max-width: 768px)"); return ( <div style={{ display: "flex", justifyContent: "space-around", alignItems: "flex-start", }} > <MasterList /> {!isSmallScreen && ( <div> <h3>Detail</h3> <Outlet /> </div> )} </div> ); }
问题描述
- 大屏(平板/电脑):主列表和详情页(Page1等)并排显示,此功能已实现。
- 小屏(手机):仅显示主列表,点击链接后需展示对应详情页,且支持返回主列表,但目前该功能未实现。
请问是否需要给MasterList.jsx中的Link添加额外属性?
// MasterList.jsx import { Link } from "react-router-dom"; const MasterList = () => { const pages = [ { id: 1, title: "Page 1" }, { id: 2, title: "Page 2" }, { id: 3, title: "Page 3" }, { id: 4, title: "Page 4" }, ]; return ( <div> <h2>Master List</h2> <nav> <ul> {pages.map((page) => ( <li key={page.id}> <Link to={"/page" + page.id}>{page.title}</Link> </li> ))} </ul> </nav> </div> ); }; export default MasterList;
// App.jsx import { Route, RouterProvider, createBrowserRouter, createRoutesFromElements, } from "react-router-dom"; import Page1 from "./pages/Page1"; import Page2 from "./pages/Page2"; import Page3 from "./pages/Page3"; import Page4 from "./pages/Page4"; import RootLayout from "./layouts/RootLayout"; const router = createBrowserRouter( createRoutesFromElements( <Route path="/" element={<RootLayout />}> <Route path="page1" element={<Page1 />} /> <Route path="page2" element={<Page2 />} /> <Route path="page3" element={<Page3 />} /> <Route path="*" element={<Page4 />} /> </Route> ) ); const App = () => { return <RouterProvider router={router} />; }; export default App;
解决方案
不需要给Link添加任何额外属性,问题根源在RootLayout的渲染逻辑——窄屏时你完全隐藏了Outlet,导致路由切换到详情页后没有容器渲染内容。
调整RootLayout的逻辑,让它根据屏幕尺寸和当前路由动态切换显示内容:
// 修改后的 RootLayout.jsx import { Outlet, useLocation, useNavigate } from "react-router"; import { useMediaQuery } from "@react-hook/media-query"; import MasterList from "../components/MasterList"; export default function RootLayout() { const isSmallScreen = useMediaQuery("(max-width: 768px)"); const location = useLocation(); const navigate = useNavigate(); const isOnDetailPage = location.pathname !== "/"; // 小屏设备逻辑 if (isSmallScreen) { if (isOnDetailPage) { // 详情页:显示返回按钮+详情内容 return ( <div> <button onClick={() => navigate("/")}>返回主列表</button> <h3>Detail</h3> <Outlet /> </div> ); } else { // 根路径:仅显示主列表 return <MasterList />; } } // 大屏设备逻辑:保持主列表与详情页并排 return ( <div style={{ display: "flex", justifyContent: "space-around", alignItems: "flex-start", }} > <MasterList /> <div> <h3>Detail</h3> <Outlet /> </div> </div> ); }
核心逻辑说明
- 用
useLocation判断当前是否处于详情页路由 - 小屏时:
- 若在详情页,渲染返回按钮+详情内容(通过
Outlet) - 若在根路径,仅渲染主列表
- 若在详情页,渲染返回按钮+详情内容(通过
- 大屏时维持原有并排布局不变
这样调整后,小屏点击Link会正常跳转到详情页,点击返回按钮能回到主列表,完全符合需求。
内容的提问来源于stack exchange,提问作者Alexander Farber
相关产品推荐
相关产品推荐

