I'm recently started using React Router v6 but facing weird behavior. Nothing is rendering on the "/" route rest all the routes are working perfectly fine. I'm not getting what I'm doing wrong.
But if I add a redirection logic in the "/" route then it is working I mean it's redirecting to some other route. but I want to show a component in the "/" route also.
My Code
package.json "react": "^18.0.0", "react-dom": "^18.0.0", "react-redux": "^8.0.1", "react-router-dom": "^6.3.0",
index.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import NavigationRoutes from './routes';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<BrowserRouter>
<NavigationRoutes />
</BrowserRouter>
);
NavigationRoutes.js
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import Layout from '../components/Layout/Layout';
import ProtectedRoute from './privateRoute';
import { appRoutes, routesWithoutLayout } from './routes';
const NavigationRoutes = () => (
<Routes>
{routesWithoutLayout.map(({ protected: _protected, id, path, element }) =>
_protected ? (
<Route key={id} path='/' element={<ProtectedRoute />}>
<Route path={path} element={element} />
</Route>
) : (
<Route key={id} path={path} element={element} />
)
)}
<Route path='/' element={<Layout />}>
{appRoutes.map(({ protected: _protected, id, path, element }) =>
_protected ? (
<Route key={id} path='/' element={<ProtectedRoute />}>
<Route path={path} element={element} />
</Route>
) : (
<Route key={id} path={path} element={element} />
)
)}
</Route>
<Route path='*' element={<p>Page not found</p>} />
</Routes>
);
export default NavigationRoutes;
ProtectedRoute.js
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
const ProtectedRoute = ({ children }) => {
const isAuthenticated = localStorage.getItem('SOME-TOKEN');
if (isAuthenticated) {
return children || <Outlet />;
}
return <Navigate replace to='/login' />;
};
export default ProtectedRoute;
Route.js
import React from 'react';
import Home from '../pages/home';
import Login from '../pages/login';
import ProtectedPage from '../pages/protected';
import Signup from '../pages/signup';
export const appRoutes = [
{
id: 'SOME_UNIQUE_STRING',
path: '/protected',
protected: true, // if true then login required to access the route
element: <ProtectedPage />,
},
{
id: 'Home',
path: '/',
protected: false, // if true then login required to access the route
element: <Home />,
},
];
export const routesWithoutLayout = [
{
id: 'Signup',
path: '/signup',
protected: true, // if true then login required to access the route
element: <Signup />,
},
{
id: 'Login',
path: '/login',
protected: false, // if true then login required to access the route
element: <Login />,
},
];
Layout.js
import React from 'react';
import { Outlet } from 'react-router-dom';
const Layout = () => (
<div>
Layout
<Outlet />
</div>
);
export default Layout;
But if I mention the /
route explicitly in Route then it will work. But in my view, it's redundant and also illogical as I'm maintaining a separate file for routing.
Working Code for NavigationRoute.js (index file in route folder)
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import Layout from '../components/Layout/Layout';
import Home from '../pages/home';
import ProtectedRoute from './privateRoute';
import { appRoutes, routesWithoutLayout } from './routes';
const NavigationRoutes = () => (
<Routes>
// redundant and also illogical
<Route path='/' element={<Layout />}>
<Route path='/' element={<Home />} />
</Route>
{routesWithoutLayout.map(({ protected: _protected, id, path, element }) =>
_protected ? (
<Route key={id} path='/' element={<ProtectedRoute />}>
<Route path={path} element={element} />
</Route>
) : (
<Route key={id} path={path} element={element} />
)
)}
<Route path='/' element={<Layout />}>
{appRoutes.map(({ protected: _protected, id, path, element }) =>
_protected ? (
<Route key={id} path='/' element={<ProtectedRoute />}>
<Route path={path} element={element} />
</Route>
) : (
<Route key={id} path={path} element={element} />
)
)}
</Route>
<Route path='*' element={<p>Page not found</p>} />
</Routes>
);
export default NavigationRoutes;
My folder structure
Thanks for the help
CodePudding user response:
You are calling NavigationRoutes
in your index.js
file but you have not imported it.
CodePudding user response:
It seems you are over-complicating the routes a bit, specifically what you are wanting to be a layout route.
Using a layout route and Outlet
components
The layout routes should omit the path
prop. This allows the nested Route
components to render their element
prop into the layout route component's Outlet
(i.e. both Layout
and ProtectedRoute
component's Outlet
).
Example:
const NavigationRoutes = () => (
<Routes>
{routesWithoutLayout.map(({ protected, id, path, element }) =>
protected ? (
<Route key={id} element={<ProtectedRoute />}>
<Route path={path} element={element} />
</Route>
) : (
<Route key={id} path={path} element={element} />
)
)}
<Route element={<Layout />}>
{appRoutes.map(({ protected, id, path, element }) =>
protected ? (
<Route key={id} element={<ProtectedRoute />}>
<Route path={path} element={element} />
</Route>
) : (
<Route key={id} path={path} element={element} />
)
)}
</Route>
<Route path='*' element={<p>Page not found</p>} />
</Routes>
);
Or conditionally render a route with the PrivateRoute
or Outlet
as the element
.
const NavigationRoutes = () => (
<Routes>
{routesWithoutLayout.map(({ protected, id, path, element }) => {
const LayoutWrapper = protected ? ProtectedRoute : Outlet;
return (
<Route key={id} element={<LayoutWrapper />}>
<Route path={path} element={element} />
</Route>
);
})}
<Route element={<Layout />}>
{appRoutes.map(({ protected, id, path, element }) => {
const LayoutWrapper = protected ? ProtectedRoute : Outlet;
return (
<Route key={id} element={<LayoutWrapper />}>
<Route path={path} element={element} />
</Route>
);
})}
</Route>
<Route path='*' element={<p>Page not found</p>} />
</Routes>
);
Using wrapper components and the children
prop
The code could also be simplified by rendering the ProtectedRoute
as a wrapper component instead of a layout route component. This utilizes the children
prop instead of using the Outlet
component.
Example:
const NavigationRoutes = () => (
<Routes>
{routesWithoutLayout.map(({ protected, id, path, element }) => {
const Wrapper = protected ? ProtectedRoute : React.Fragment;
return (
<Route
key={id}
path={path}
element={<Wrapper>{element}</Wrapper>}
/>
);
})}
<Route element={<Layout />}>
{appRoutes.map(({ protected, id, path, element }) => {
const Wrapper = protected ? ProtectedRoute : React.Fragment;
return (
<Route
key={id}
path={path}
element={<Wrapper>{element}</Wrapper>}
/>
);
})}
</Route>
<Route path='*' element={<p>Page not found</p>} />
</Routes>
);
Reduce further by moving the protected
condition into the ProtectedRoute
and handle the conditional rendering of the element
.
const ProtectedRoute = ({ children, protected }) => {
const isAuthenticated = localStorage.getItem('SOME-TOKEN');
if (!protected || isAuthenticated) {
return children || <Outlet />;
}
return <Navigate replace to='/login' />;
};
...
const NavigationRoutes = () => (
<Routes>
{routesWithoutLayout.map(({ protected, id, path, element }) => (
<Route
key={id}
path={path}
element={<ProtectedRoute {...{ protected }}>{element}</ProtectedRoute>}
/>
))}
<Route element={<Layout />}>
{appRoutes.map(({ protected, id, path, element }) => (
<Route
key={id}
path={path}
element={<ProtectedRoute {...{ protected }}>{element}</ProtectedRoute>}
/>
))}
</Route>
<Route path='*' element={<p>Page not found</p>} />
</Routes>
);
Use an improved routes configuration object and the useRoutes
hook
Resturcture the routes config to more closely match what/how you want to render the routes and use the useRoutes
hook
Example:
export const appRoutes = [
{
// routes with Layout
element: <Layout />,
children: [
{
// protected routes
element: <ProtectedRoute />,
children: [
{
path: '/protected',
element: <ProtectedPage />,
},
... other protected routes inside Layout
],
},
// unprotected routes
{
path: '/',
element: <Home />,
},
... other unprotected routes inside Layout
],
},
// routes w/o Layout
{
// protected routes
element: <ProtectedRoute />,
children: [
{
path: '/signup',
element: <Signup />,
},
... other protected routes outside Layout
],
},
// unprotected routes
{
path: '/login',
element: <Login />,
},
... other unprotected routes outside Layout
];
NavigationRoutes
import { useRoutes } from 'react-router-dom';
import { appRoutes } from './routes';
const NavigationRoutes = () => {
const routes = useRoutes(appRoutes);
return routes;
};
export default NavigationRoutes;