skip to Main Content

AppRoutes:

function AppRoutes(): JSX.Element {
  return (
    <Routes>
      <Route path="/" element={<Auth />} />

      <Route element={<RequiredAuth />}>
        <Route path="/home" element={<Home />} />
      </Route>
    </Routes>
  );
}

private routes checks

import { Navigate, Outlet } from "react-router-dom";
import useAuth from "./hooks/useAuth";

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

  console.log("USERAUTH", user);
  return user ? <Outlet /> : <Navigate to="/" replace />;
};
export default RequiredAuth;

useAuth Hook with zustand store

import { create } from "zustand";
import axios, { AxiosResponse } from "axios";
import apiSecure, { api } from "../libs/axios";

type user = {
  id: string;
};

interface useAuthStore {
  user: user | null;
  signIn: (username: string, password: string) => Promise<void>;
  fetchCurrentUser: () => Promise<void>;
}
interface AuthResponse {
  code: number;
  error: boolean;
  message: string;
  data: {
    email: string;
    id: string;
    name: string;
    token: string;
    username: string;
  };
  errors?: [];
}

const useAuth = create<useAuthStore>((set) => ({
  user: null,
  signIn: async (username: string, password: string): Promise<void> => {
    // login process
  },
  fetchCurrentUser: async (): Promise<void> => {
    try {
      const response: AxiosResponse<AuthResponse> = await apiSecure.get(
        `/auth/currentuser`
      );

      const userData = response.data.data;

      set({ user: { id: userData.id } });
      console.log("SETUP", { id: userData.id });
    } catch (error: unknown) {
      // Handle authentication errors
      
    }
  },
}));

export default useAuth;
function App(): JSX.Element {
  const { fetchCurrentUser, user } = useAuth();
  console.log(user);

  useEffect(() => {
    async function auth() {
      await fetchCurrentUser();
    }
    auth();
  }, [fetchCurrentUser]);

  // useEffect(() => {}, [fetchCurrentUser])
  return (
    <>
      <AppRoutes />
    </>
  );
}

export default App;

I’m trying to build authentication with react-router-v6 and have used zustand for state management and verify user using jwt token for which calling fetchcurrentuser (/auth/currentuser api) and updating user state in useAuth with zustand,
and protected routes /home with RequiredAuth but it’s accessible even if user verified and state updated successfully, or can say updated state with zustand is updated in requiredAuth function.

it’s always redirect / what’s wrong i am doing?

Please help, I’m relative new to react and this would be much appreciated. Thanks in advance!

2

Answers


  1. Enclose your Routes with BrowserRouter.

    <BrowserRouter>
    <Routes>
      <Route path="/" element={<Auth />} />
    
      <Route element={<RequiredAuth />}>
        <Route path="/home" element={<Home />} />
      </Route>
    </Routes>
    </BrowserRouter>
    

    This may be the reason for the error.

    Login or Signup to reply.
  2. The user state starts off already in the "unauthenticated" condition, so on any initial rendering of the component the redirect to "/" will be effected.

    With route protection and authentication schemes like this you effectively have 3 "states":

    • Confirmed authenticated
    • Confirmed unauthenticated
    • Unknown, e.g. "we don’t know yet"

    It’s this third state that is important. You should start from the unknown state and wait until authentication confirmation to switch to either of the other two confirmed states.

    Example:

    interface useAuthStore {
      user?: user | null; // <-- optional to allow undefined
    
      signIn: (username: string, password: string) => Promise<void>;
      fetchCurrentUser: () => Promise<void>;
    }
    
    const useAuth = create<useAuthStore>((set) => ({
      user: undefined, // <-- initially "unknown"
    
      signIn: async (username: string, password: string): Promise<void> => {
        // login process
      },
      fetchCurrentUser: async (): Promise<void> => {
        try {
          const response: AxiosResponse<AuthResponse> = await apiSecure.get(
            `/auth/currentuser`
          );
    
          const { data } = response.data;
    
          if (data.id) {
            set({ user: { id: data.id } });
            console.log("SETUP", { id: data.id });
          } else {
            set({ user: null }); // <-- no id, set null
          }
        } catch (error: unknown) {
          // Handle authentication errors
          set({ user: null }); // <-- auth failed, set null
        }
      },
    }));
    
    const RequiredAuth = () => {
      const { user } = useAuth();
    
      if (user === undefined) {
        return null; // <-- or loading indicator/spinner/etc
      }
    
      return user ? <Outlet /> : <Navigate to="/" replace />;
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search