如何在Remix中为body添加随页面动态变化的类名?
问题
需要给root.jsx中的<body>标签添加随页面变化的类名,要求每个路由页面手动指定类名:
- 首页(_index.jsx)类名为
company homepage - 餐厅页(restaurants.jsx)类名为
venue company
之前尝试通过loader从API获取pageClassName数据设置类名,但部分页面无该数据,因此需要从路由页面手动传递类名参数到root.jsx。相关代码如下:
root.jsx
export default function App() { return ( <html lang='en'> <head> <meta charSet='utf-8' /> <meta name='viewport' content='width=device-width, initial-scale=1' /> <link rel='preconnect' href='https://fonts.googleapis.com' /> <link rel='preconnect' href='https://fonts.gstatic.com' crossOrigin='anonymous' /> <link href='https://fonts.googleapis.com/css?family=Montserrat:wght@100,300,400,500,700&display=swap' rel='stylesheet' /> <Meta /> <Links /> </head> <body> <Outlet /> <ScrollRestoration /> <Scripts /> <LiveReload /> </body> </html> ); }
_index.jsx
export default function Index() { const { startPage } = useLoaderData() const page = startPage.page.sections; return ( <main id='main'> <PageComponents page={page} /> </main> ); }
restaurants.jsx
export default function Restaurants() { const { restaurants } = useLoaderData(); return ( <> <PageComponents page={restaurants.sections} /> <footer className='footer' /> </> ); }
PageComponents.jsx
import loadable from "@loadable/component"; const components = { logo: loadable(() => import("./Logo")), intro: loadable(() => import("./Intro")), languageSwitch: loadable(() => import("./LanguageSwitch")), venueGroup: loadable(() => import("./VenueGroup")), }; export default function PageComponents({ page }) { return ( <main> {page .filter(({ type }) => !!components[type]) .map(({ data, type }) => { const Component = components[type]; return ( <div key={type}> <Component data={data} /> </div> ); })} </main> ); }
解决方案
提供两种可行实现方式,按需选择:
方式一:通过Outlet上下文传递类名(页面级单独控制)
利用React Router的useOutletContext能力,让每个页面主动传递类名给根组件:
- 修改root.jsx
引入useOutletContext获取页面传递的类名,绑定到<body>标签:
import { useOutletContext } from "react-router-dom"; export default function App() { const pageClassName = useOutletContext() || ""; return ( <html lang='en'> <head> <meta charSet='utf-8' /> <meta name='viewport' content='width=device-width, initial-scale=1' /> <link rel='preconnect' href='https://fonts.googleapis.com' /> <link rel='preconnect' href='https://fonts.gstatic.com' crossOrigin='anonymous' /> <link href='https://fonts.googleapis.com/css?family=Montserrat:wght@100,300,400,500,700&display=swap' rel='stylesheet' /> <Meta /> <Links /> </head> <body className={pageClassName}> <Outlet /> <ScrollRestoration /> <Scripts /> <LiveReload /> </body> </html> ); }
- 修改首页(_index.jsx)
添加类名传递逻辑:
import { useOutletContext } from "react-router-dom"; export default function Index() { const { startPage } = useLoaderData() const page = startPage.page.sections; useOutletContext("company homepage"); return ( <main id='main'> <PageComponents page={page} /> </main> ); }
- 修改餐厅页(restaurants.jsx)
同样添加类名传递:
import { useOutletContext } from "react-router-dom"; export default function Restaurants() { const { restaurants } = useLoaderData(); useOutletContext("venue company"); return ( <> <PageComponents page={restaurants.sections} /> <footer className='footer' /> </> ); }
方式二:路由配置中统一管理类名(集中维护)
在路由配置里给每个路由添加meta字段存储类名,根组件通过路由匹配获取类名:
- 修改路由配置文件(示例:routes.jsx)
import { createBrowserRouter } from "react-router-dom"; import Index from "./_index"; import Restaurants from "./restaurants"; export const router = createBrowserRouter([ { path: "/", element: <Index />, meta: { pageClassName: "company homepage" } }, { path: "/restaurants", element: <Restaurants />, meta: { pageClassName: "venue company" } } ]);
- 修改root.jsx
引入路由配置,匹配当前路由获取类名:
import { useLocation, matchRoutes } from "react-router-dom"; import { router } from "./routes"; export default function App() { const location = useLocation(); const matchedRoute = matchRoutes(router.routes, location)[0]?.route; const pageClassName = matchedRoute?.meta?.pageClassName || ""; return ( <html lang='en'> <head> <meta charSet='utf-8' /> <meta name='viewport' content='width=device-width, initial-scale=1' /> <link rel='preconnect' href='https://fonts.googleapis.com' /> <link rel='preconnect' href='https://fonts.gstatic.com' crossOrigin='anonymous' /> <link href='https://fonts.googleapis.com/css?family=Montserrat:wght@100,300,400,500,700&display=swap' rel='stylesheet' /> <Meta /> <Links /> </head> <body className={pageClassName}> <Outlet /> <ScrollRestoration /> <Scripts /> <LiveReload /> </body> </html> ); }
这种方式无需修改页面组件,类名统一在路由配置中维护,适合路由较多的场景。
内容的提问来源于stack exchange,提问作者Viktoria Tarasova
相关产品推荐
相关产品推荐

