Samyak jain
Samyak jain

Reputation: 137

Conditional Routes in react router dom v6

I am making a mern application with login/registration system and the dashboard and the other routes that will only be accessible when someone logs in now the problem is i was trying to write a if condition in react router dom so that all the routes inside the dashboard could be resitricted till the user logs in , and i use 'useNavigate' hook to go back to login page if user is not logged in but the application gives me error saying useNavigate() may be used only in the context of a component , but i used the same hook in my other component too where i didnt used any router and it worked fine there and so i am not able to understand what to do , also i want to know how can i put the component name inside a variable so that if i call the function i can put the component name in it and later put that varibale like this <component_name/> and it should change its value , here is my code:-

import Navbar from "./components/navbar/Navbar";
import Home from "./components/Home/Home";
import 'bootstrap/dist/css/bootstrap.min.css'
import Forgot_password from "./components/login_and_reg/Forgot_password/Forgot_password";
import Weather from "./components/Weather/Weather";
import Landing_page from './components/login_and_reg/Landing_page/Landing_page'
import {
    BrowserRouter as Router,
    Route,
    Routes,
    useNavigate
  } from "react-router-dom";
import Verification from "./components/login_and_reg/Verification/Verification";
import Protected_routes from './components/Protected_routes'
import { useSelector } from "react-redux";

function App() {
    const loggedin = useSelector(state => state.loggedin)
    const Navigate = useNavigate();
    const rememberMe = localStorage.getItem('rememberMe')

    function checkroute(Component_name){
        if (rememberMe=='true') {
            return <Component_name/> 
        } else {
            console.log(loggedin)
            if(loggedin =='loggedin'){
                return <Component_name/> 
            }
            else{
                Navigate('/')
            }
            
        }
    }
return (
    <>
            <Router>
                {/* <Navbar/> */}
                <Routes>
                    <Route path="/weather" element={checkroute(Weather)}></Route>
                    <Route exact path="/" element={<Protected_routes/>}></Route>
                    <Route path="/verify/:first_name/:email" element={<Verification/>}></Route>
                    <Route path="/forgot_password" element={<Forgot_password/>}></Route>
                    {/* <Route exact path="/home" element={<Protected_routes/>}></Route> */}


                </Routes>
            </Router>
                
            
            
        
    </>
);
}

I also made a protected route only for login purpose but i dont know how to use it for all the components if it is possible then here is code of that component:-

import React, { Component } from 'react';
import { useSelector } from 'react-redux';
import {Navigate, Route , useNavigate} from 'react-router-dom';
import Home from './Home/Home';
import Landing_page from './login_and_reg/Landing_page/Landing_page';


const Protected_routes = () => {
    const loggedin = useSelector(state => state.loggedin)
    const Navigate = useNavigate();
    const rememberMe = localStorage.getItem('rememberMe')
    if (rememberMe=='true') {
        return <Home/> 
    } else {
        if(loggedin=='loggedin'){
            return <Home/> 
        }
        else{
            return <Landing_page/> 
        }
        
    }
    
}

export default Protected_routes

export default App;

Upvotes: 1

Views: 4938

Answers (2)

Dante Nu&#241;ez
Dante Nu&#241;ez

Reputation: 521

For those who are using createBrowserRouter here is another alternative.

You can make a combination of exaucae's answer just with this one by placing the component inside a children.

In this case when the user navigates to a route that is not allowed, he will get a 404.

Create a custom hook wrapping createBrowserRouter

Custom hook:

const useRouters = () => {
   const [loggedIn, setLoggedIn ] = useState(true) 


   const PRIVATE_ROUTES: RouteObject[] = [
    {
      action: loginAction,
      element: <Login />,
      path: '/'
    },
    {
      path: routesPaths.appCatalog,
      element: {loggedIn ? <Start /> : <Navigate replace to={"/"} />}
    }, // <----- or
    ...( loggedIn ? [
      {
        path: routesPaths.appCatalog,
        element: <Start />
      }
    ] : [])
  ]

  const PUBLIC_ROUTES: RouteObject[] = []

  return createBrowserRouter([...PRIVATE_ROUTES, ...PUBLIC_ROUTES])
}

export default useRouters

Then warp the RouterProvider inside other Provider and use the custom hook

main.tsx:

const ContextsProvider = () => {
  const ROUTER = useRouters()

  return (
     <OthersProvider>
        <RouterProvider router={ROUTER} />  // <---- or only this alone
     <OthersProvider />
  )
}

export default ContextsProvider

Upvotes: 0

exaucae
exaucae

Reputation: 2675

What you'd likely use a PrivateRoute component to wrap your secured pages. It will render the desired page if not logged in, or redirect to the login page.

Here's the flow:

1. Define a private route component:

// PrivateRoute.ts

import { Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
import  React from 'react';


export function PrivateRoute({ children }: { children: React.ReactElement }) {

// the user verification logic is up to your application
// this is an example based on your code above 
    const loggedin = useSelector(state => state.loggedin);
    const rememberMe = localStorage.getItem('rememberMe');

   if (rememberMe==='true') {
    return <Navigate to={'/home'} />;
    }
  else if (loggedin==='loggedin'){
// render the wrapped page
    return children;
}
  else {
// user not logged in, redirect to the Login page which is unprotected
    return <Navigate to={'/login'} />;
  }
}

2. And use it in your router :

I assume you want to protect your Home and Wheater pages here. You can customize it to your own logic.


import React from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';
import { PrivateRoute } from './PrivateRoute';
import { Home } from "./my-home-page";
import { Wheater } from "my-wheater-page";
import { Login } from "my-login-page";

export const AppRoutes = () => {
  return (
    <Routes>
        <Route
          path='home'
          element={
            <PrivateRoute>
                <Home/>
            </PrivateRoute>
          }
        />
        <Route
          path='login'
          element={
              <Login/>
          }
        />
        <Route
          path='wheater'
          element={
            <PrivateRoute>
                <Wheater />
            </PrivateRoute>
          }
        />
    </Routes>
  );
};

Upvotes: 2

Related Questions