Reputation: 1408
I am struggling to render my routes list with React router v6. I have already read the docs and I am aware of a new structure of routing, but currently having a problem rendering list (nested) of routes.
export default [{
path: '/',
element: Root,
routes: [{
path: REGISTER,
element: Register,
isProtected: true,
}],
}];
export const RouteMatcher = ({ routes }) => {
return routes.map((route, index) => {
if (route.element) {
return (
<Route key={index} path={route.path} element={<route.element />}>
{route.routes ? (
<Route path={route.path} element={<RouteMatcher routes={route.routes} />
) : null}
</Route>
);
}
return null;
});
};
<BrowserRouter>
<Routes>
<Route path="/" element={<RouteMatcher routes={routes} />} />
</Routes>
</BrowserRouter>
It's not obvious to me what's going on here even though the error message has a clear explanation of the issue. Even I try this way it doesn't work.
<BrowserRouter>
<Routes>
<Route path="/">
{routes.map((route, index) => {
if (route.element) {
return (
<Route key={index} path={route.path} element={<route.element />}>
{route.routes ? (
<Route path={route.path} element={<RouteMatcher routes={route.routes} indexPathname={indexPathname} />} />
) : null}
</Route>
);
}
return null;
})}
</Route>
</Routes>
</BrowserRouter>
As you can see Route
is always a child of Routes
or Route
(when nested).
** UPDATED ** Here is the react router v5 implementation
<Provider store={store}>
<GlobalStyles />
<AppContentWrapper>
<RouteHandler />
</AppContentWrapper>
</Provider>
Route handler component
<BrowserRouter>
{generateRouteMatches(
routes,
indexPathname,
auth.isLoading,
auth.isLoggedIn
)}
</BrowserRouter>
Route generator component
export const generateRouteMatches = (
baseRoutes: IAppRoute[],
indexPathname: string,
userPermissions: PermissionShape[],
isLoading: boolean,
isLoggedIn: boolean,
) => {
AccessControl.createPermissions(userPermissions);
return baseRoutes.map((route) => (
<MatchRoutes
indexPathname={indexPathname}
authIsLoading={isLoading}
isLoggedIn={isLoggedIn}
key={route.path}
{...route}
/>
));
};
MatchRoutes component with RouteRenderer
function MatchRoutes({ location, ...route }: any) {
const routePermissions = AccessControl.getPermissions(route.zone);
if (!routePermissions.canRead && route.isProtectedRoute) {
return <Route {...omit(route, ['component'])} component={() => {
return <div className="centerXY">You dont have permissions to view this page</div>;
}} />;
}
return (
<Route {...omit(route, ['component'])} render={(props) => (
<RouteRenderer {...props} route={route} location={location} />
)} />
);
}
function RouteRenderer({ route, ...props }: any) {
const location = useLocation();
if (location?.pathname === '/') {
return (
<Redirect
to={{
pathname: route.indexPathname,
state: { from: location },
}}
/>
);
}
if (route.isProtectedRoute && !route.isLoggedIn && !route.authIsLoading) {
return (
<Redirect to={{
pathname: '/login',
state: { from: location },
}}/>
);
}
if (route.component) {
return (
<route.component
{...props}
params={props.match.params}
routes={route.routes}
>
{route.routes
? route.routes.map((cRoute, idx) => (
<MatchRoutes
authIsLoading={route.authIsLoading}
isLoggedIn={route.isLoggedIn}
key={idx}
{...cRoute}
/>
))
: null
}
</route.component>
);
} else if (route.routes) {
return (
<>
{route.routes.map((cRoute, idx) => (
<MatchRoutes
authIsLoading={route.authIsLoading}
isLoggedIn={route.isLoggedIn}
key={idx}
{...cRoute}
/>
))}
</>
);
} else {
return null;
}
}
export default MatchRoutes;
Upvotes: 0
Views: 897
Reputation: 202608
In the first example the RouteMatcher
component is rendering a Route
component directly. The mapped routes it is rendering need to be wrapped in a Routes
component.
export const RouteMatcher = ({ routes }) => {
return (
<Routes>
{routes
.filter(route => route.element)
.map((route, index) => {
return (
<Route
key={index}
path={route.path}
element={<route.element />}
>
{route.routes && (
<Route
path={route.path}
element={<RouteMatcher routes={route.routes} />}
/>
)}
</Route>
);
})
}
</Routes>
);
};
I suspect something similar is occurring int he second code example as well.
I suggest using a better formed routes configuration and use the useRoutes
hook.
Example:
export default [{
path: '/',
element: <Root />,
children: [
{
element: <AuthOutlet />,
children: [
{
path: REGISTER,
element: <Register />,
},
... other routes to protect ...
],
... other unprotected routes ...
},
],
}];
...
import appRoutes from '../path/to/routes';
...
const routes = useRoutes(appRoutes);
...
<BrowserRouter>
{routes}
</BrowserRouter>
Upvotes: 1