Home > OS >  React router v6 not rendering anything on "/" route. Rest all route is working fine
React router v6 not rendering anything on "/" route. Rest all route is working fine

Time:06-16

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

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;
  • Related