Following up from my question React router v6 and relative links from page within route , I'm trying to refactor the routes in our app to be more nested.
Trouble is that it doesn't seem possible to render a Route element recursively from data, because react-router insists that Route is directly inside Route and not wrapped in another component, and I cannot see how to render recursively (to arbitrary depth) any other way.
Reproduction on codesandbox .
import React from "react";
import { BrowserRouter, Routes, Route} from "react-router-dom";
import "./styles.css";
function GenericPage() {
return <div className="page">Generic page</div>;
}
const nav = {
slug: "",
title: "Home",
children: [
{
slug: "foo",
title: "Foo"
},
{
slug: "bar",
title: "Bar"
}
]
};
const RecursiveRoute = ({ node }) => {
return (
<Route path={node.slug} element={<GenericPage />}>
{node.children?.map((child) => (
<RecursiveRoute node={child} />
))}
</Route>
);
};
export default function App() {
return (
<BrowserRouter>
<Routes>
<RecursiveRoute node={nav} />
</Routes>
</BrowserRouter>
);
}
Error from react-router:
[RecursiveRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
As the error indicates, you can't render Route
components directly, they must be rendered directly by a Routes
component, or another Route
component in the case of nested routes.
Refactor RecursiveRoute
to render a Routes
component with a route for the current node and then map the node's children to routes that render the RecursiveRoute
as an element
.
Example:
function GenericPage({ title }) {
return (
<div className="page">
{title} page
</div>
);
}
const RecursiveRoute = ({ node }) => (
<Routes>
<Route
path={`${node.slug}/*`}
element={<GenericPage title={node.title} />}
/>
{node.children?.map((child) => (
<Route
key={child.slug}
element={<RecursiveRoute key={child.slug} node={child} />}
/>
))}
</Routes>
);
I strongly suggest not trying to roll your own custom route configuration and renderer, use the useRoutes
hook instead to do all the heavy lifting for you.
Example:
Refactor the navigation config:
const nav = [
{
path: "/",
element: <GenericPage title="Home" />,
children: [
{
path: "foo",
element: <GenericPage title="Foo" />
},
{
path: "bar",
element: <GenericPage title="Bar" />
}
]
}
];
Pass the config to the useRoutes
hook and render the result:
const routes = useRoutes(nav);
...
return routes;
I had the same puzzle to solve. In general I solve it by passing a function in Routes component. Here is my solution with few code snippets.
// in Routes.ts
interface IRoutes {
path: string
component: JSX.Element
children?: IRoutes[]
}
const routes: IRoutes[] = [
{
path: 'warehouse'
component: <WarehousePage />
children: [
{
path: 'products'
component: <ProductsPage />
},
{
path: 'units'
component: <UnitsPage />
},
]
},
]
// in AppRouter.tsx
const renderRoutesRecursive = (routes: IRoutes[]) =>
routes.map((route, index) =>
route.children ? (
renderRoutesRecursive(route.children)
) : (
<Route
key={index}
path={route.path}
element={route.component}
/>
),
)
const renderRoutes = useMemo(() => renderRoutesRecursive(routes), [routes])
return (
<Routes>
<Route path='/' element={<Layout />}>
{renderRoutes}
</Route>
</Routes>
)
// in Layout.tsx
const Layout = () => {
return (
<>
<Header />
<Navigation />
<Main>
<Outlet />
</Main>
<Footer />
</>
)
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.