Home > Software engineering >  Protected Routes in react router dom 6
Protected Routes in react router dom 6

Time:11-27

I created simple user context in react:

UserProvider

export const AuthContext = createContext();


export const AuthProvider = ({children}) => {
  const [user, setUser] = useState(null);

  useEffect(() => {
   
      //Call server
      const loginUser = {
        userId: 1,
        role: "Admin"
      }
      setUser(loginUser)
    }, []);

  return (
    <AuthContext.Provider value={{user}}  >{children}</AuthContext.Provider>
  );
};

useAuth

const useAuth = () => {
    const user = useContext(AuthContext);
    if (user === undefined) {
      throw new Error('useAuth must be used within an AuthProvider');
    }
    return user;
};

export default useAuth;

App

  return (
    <AuthProvider>
      <BrowserRouter>
        <Routes>
          <Route path="/Login" element={<Login/>}/>
          <Route element={<ProtectedRoutes />}>
            <Route path="/User/Profile" element={<Profile/>}/>
          </Route>
        </Routes>
      </BrowserRouter>
    </AuthProvider>
  );

Protected Routes

const ProtectedRoutes = () => {
  const {user} = useAuth();

  return(
    user ? <Outlet/> : <Navigate to="/login"/>
  )

}

export default ProtectedRoutes

Everything works fine, but when I refresh the page in User/Profile when user is logged,it anyway redirects me to login. Console log in Protected routes when i refresh the page looks like this:

null
{userId: 1, role: 'Admin'}

I know that this problem probably occurs because "user" is null when first rendered, but I have no idea how to fix it.Could anyone tell me how i can improve it?

CodePudding user response:

You can keep a app state and redirect only when app authenticated

Here is a good article by Kent C. Dodds on how to handle authentication

export const AuthContext = createContext();


export const AuthProvider = ({children}) => {

  const [appState, setAppState] = useState({ user: null, state: 'loading' });

  useEffect(() => {
   
      //Call server
      const loginUser = {
        userId: 1,
        role: "Admin"
      }
      setUser({
         user: loginUser,
         state: 'loaded'
      })
    }, []);

  return (
    <AuthContext.Provider value={appState}  >{children}</AuthContext.Provider>
  );
};


const useAuth = () => {
    const state = useContext(AuthContext);
    if (state === undefined) {
      throw new Error('useAuth must be used within an AuthProvider');
    }
    return { ...state };
};

export default useAuth;

handling app state in ProtectedRoutes

const ProtectedRoutes = () => {

  const { user, state } = useAuth();

  if (state === 'loading') {
    return <div>Loading...</div>
  }

  return(
    user ? <Outlet/> : <Navigate to="/login"/>
  )

}

export default ProtectedRoutes

Or rendering the protected routes only after app is loaded

const AppRoutes = () => {

   const { user, state } = useAuth();
   
   if (state === 'loading') {
       return <div>Loading...</div>
   }   

   return user ? 
     <AuthenticatedRoutes>
     : UnAuthenticatedRoutes />

}

  return (
    <AuthProvider>
      <BrowserRouter>
            <AppRoutes />
        </Routes>
      </BrowserRouter>
    </AuthProvider>
  );

Hope this helps in some way

  • Related