reactjsreact-routerreact-router-dom

private route in react


I'm using React Router v6 and am creating private routes for my application. i must to close all routes except login until user is authenticated

this is my private route

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

import loginAction from '@/api/AuthProvider';

export function PrivateRoute({ children }) {
  const { user } = loginAction();

  return user ? children : <Navigate to="/login" />;
}

this is my request

export default function loginAction() {
  return new Promise((resolve) => {
    resolve({
      user: {
        fullName: 'Elon Mask',
        dob: '2022-07-27T12:46:26.356Z',
        email: 'user@gmail.com',
        defaultCurrency: 'USD'
      },
      token: 'DTYHKL57HGGJ'
    });
  }).then((data) => {
    localStorage.setItem('token', data.token);
    return data;
  });
}

This is my App

export const App = () => {
  return (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>
        <Routes>
          <Route path="/login" element={<Login />} />

          <Route
            element={
              <PrivateRoute>
                <AppLayout />
              </PrivateRoute>
            }
          >
            <Route
              path="/"
              element={
                <PrivateRoute>
                  <Landing />
                </PrivateRoute>
              }
            />
            <Route
              path="/about"
              element={
                <PrivateRoute>
                  <About />
                </PrivateRoute>
              }
            />
            <Route
              path="/categories"
              element={
                <PrivateRoute>
                  <Categories />
                </PrivateRoute>
              }
            />
            <Route
              path="*"
              element={
                <PrivateRoute>
                  <Fallback />
                </PrivateRoute>
              }
            />
            <Route
              path="/expenses"
              element={
                <PrivateRoute>
                  <Expenses />
                </PrivateRoute>
              }
            />
          </Route>
        </Routes>
      </BrowserRouter>
    </QueryClientProvider>
  );
};

but when i submit, it does not redirect me to the home page what i am doing wrong? Is there something I'm missing?


Solution

  • You're trying to use loginAction as a react hook when in reality it's an asynchronous function that returns a promise.

    The way that I would suggest solving this is converting the loginAction action into a react hook. Read more about hooks here: https://reactjs.org/docs/hooks-intro.html

    Essentially the two main parts of a hook are that:

    1. The name starts with the word use such as useLoginAction
    2. It can create/return state variables and when those state variables changes, components that use the hook will also re-render.

    So the first change is wrapping loginAction into a hook: If you don't understand how the useEffect hook works, look at this: https://reactjs.org/docs/hooks-effect.html

    import { useState, useEffect } from "react";
    
    function loginAction() {
      return new Promise((resolve) => {
        resolve({
          user: {
            fullName: 'Elon Mask',
            dob: '2022-07-27T12:46:26.356Z',
            email: 'user@gmail.com',
            defaultCurrency: 'USD'
          },
          token: 'DTYHKL57HGGJ'
        });
      }).then((data) => {
        localStorage.setItem('token', data.token);
        return data;
      });
    }
    
    export default function useLoginAction(){
      const [data, setData] = useState({ user: null });
    
      useEffect(() => {
       loginAction().then((response) => setData(response));
      }, []);
    
      return data;
    }
    

    After that you can use the hook in your private route function. There are less repetitive ways to conditionally render the routes but that's outside the scope of this question:

    import React from 'react';
    import { Navigate } from 'react-router-dom';
    
    import useLoginAction from '@/api/AuthProvider';
    
    export function PrivateRoute({ children }) {
      const { user } = useLoginAction();
    
      return user ? children : <Navigate to="/login" />;
    }
    

    And this should work the way you're expecting it to work from my understanding.