Home > database >  private route in react
private route in react

Time:07-28

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: '[email protected]',
        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?

CodePudding user response:

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: '[email protected]',
        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.

CodePudding user response:

You can control access to routes by some flag (isAuth) or other value. I'm doing this:

const App = () => {
    const navigate = useNavigate()
    const accessToken = useAppSelector((state) => state.authSlice.accessToken)

    useEffect(() => {
        if (accessToken === '') {
            navigate('/login', { replace: true })
        } else {
            navigate('/', { replace: true })
        }
    }, [accessToken])

    return (
        <>
            {accessToken !== '' ? (
                <Routes>
                    <Route path='/' element={<Main />} />
                    <Route path='/profile' element={<Profile />} />
                    <Route path='*' element={<NotFound />} />
                </Routes>
            ) : (
                <Routes>
                    <Route path='/login' element={<Login />} />
                    <Route path='/registration' element={<Registration />} />
                    <Route path='*' element={<NotFound />} />
                </Routes>
            )}
            <StatusBar />
        </>
    )
}

export default App
  • Related