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;

