Search code examples
javascriptreactjsreact-router-domuse-effect

Configuring private routes with react-router-dom v6


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 ?


Solution

  • 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'} />
    }