Home > database >  Configuring private routes with react-router-dom v6
Configuring private routes with react-router-dom v6

Time:07-05

When the visitor goes to / (home), I want him to be redirected to /connexion" if he is not connected. I created Private routes for that, which works fine. Now, I want to implement the logic that will redirect the user according to if he is connected or not.

I have these routes in App.jsx:

import ProtectedRoutes from './middlewares/ProtectedRoutes';

return (
    <>
      <Routes>
        <Route path="/connexion" element={<Login />} />
        <Route path="/auto-connexion" element={<AutoConnect />} />

        <Route element={<AppLayout />} >
          <Route element={<ProtectedRoutes />}>
            <Route path="/" element={<Home />} />
            <Route path="/logical-entity-selection" element={<LogicalEntitySelection />} />
            <Route path="/produits" element={<Products />} />
            <Route path="/produits/:id" element={<Product />} />
            <Route path="/actualites" element={<Articles />} />
            <Route path="/actualites/id" element={<Article />} />
            <Route path="/mes-parametres" element={<MyAccount />} />
            <Route path="/mes-outils-et-services" element={<MyToolsAndServices />} />
            <Route path='*' element={<Login />} />
          </Route>
        </Route>
      </Routes>
    </>
  );

An this ProtectedRoutes.tsx :

import { useEffect, useState } from "react"
import { Navigate, Outlet } from "react-router-dom"
import jwt_decode from "jwt-decode"
import instance from "../api/axios"

export default function ProtectedRoutes() {
    const [isLoggedIn, setIsLoggedIn] = useState(Boolean)

    const token = window.localStorage.getItem("token") || ''
    const decodedToken: any = jwt_decode(token)
    const uuid = decodedToken.uuid

    const isAuth = async () => {
        await instance.get(`/users/${uuid}`, {
            headers: {
                'Authorization': `Bearer ${token}`
            }
        }).then((res) => {
            console.log(res)
            if (res.status === 200) setIsLoggedIn(true)
            return setIsLoggedIn(false)
        })
    }

    useEffect(() => {
        isAuth()
    }, [isLoggedIn])

    return isLoggedIn ? <Outlet /> : <Navigate to={'/connexion'} />
}

The problem is that with this code, React render the Login Component because it returns always false, even if I have a status 200 after my request and set the new state to true.

How can I make my request FIRST, then set the new state for isLoggedIn, then decide to render Login component or Home component ?

I hope I made it clear. Don't hesitate to question me if not. Any help on this ?

CodePudding user response:

You would need a loading state in addition to what you have to make it work correctly, I called it isChecking. Also the below block of code that you have should be changed, because you are setting isLoggedIn to true and right after to false.

 if (res.status === 200) setIsLoggedIn(true)
 return setIsLoggedIn(false)

Solution:

import { useEffect, useState } from "react"
import { Navigate, Outlet } from "react-router-dom"
import jwt_decode from "jwt-decode"
import instance from "../api/axios"

export default function ProtectedRoutes() {
    const [isLoggedIn, setIsLoggedIn] = useState(false)
    const [isChecking, setIsChecking] = useState(true)
    const token = window.localStorage.getItem("token") || ''
    const decodedToken: any = jwt_decode(token)
    const uuid = decodedToken.uuid

    const isAuth = async () => {
        await instance.get(`/users/${uuid}`, {
            headers: {
                'Authorization': `Bearer ${token}`
            }
        }).then((res) => {
            console.log(res)
            if (res.status === 200) setIsLoggedIn(true)
            setIsChecking(false);
            return;
        })
    }

    useEffect(() => {
        isAuth()
    }, [isLoggedIn])

    if(isChecking) return <p>Checking....</p>

    return isLoggedIn ? <Outlet /> : <Navigate to={'/connexion'} />
}
  • Related