Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

When i access a react-router route directly via url, it redirects to the '/' route

Whenever I access the "/projects" route via url, it redirects to "/", but I can access it via a button for example.
I am 100% sure it’s something wrong with the AuthContext, because if I just put true on the condition arg on PrivateRoute component, it works, but if the condition come from the AuthContext, it’s buggy.

This is my Router component:

// private route, if condition is true, it render the children component,
// else, redirect to 'redirectRoute'
function PrivateRoute({ condition, redirectRoute = '/login' }) {
  const auth = useAuth();

  if(auth.loading) return null;

  return (
    condition
      ? <Outlet></Outlet>
      : <Navigate to={redirectRoute}></Navigate>
  )
}

function Router() {
  // this useAuth() is basically a useContext(AuthContext)
  const auth = useAuth();

  return (
    <BrowserRouter>
      <Routes>
        <Route
          exact
          path="/"
          element={<PrivateRoute condition={auth.isLogged}></PrivateRoute>}
        >
          <Route exact path="/" element={<Home></Home>}></Route>
        </Route>
        <Route
          exact
          path="/projects"
          element={<PrivateRoute condition={auth.isLogged}></PrivateRoute>}
        >
          <Route
            exact
            path="/projects"
            element={<Projetos></Projetos>}
          ></Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

And this is my AuthContext.js:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

export const AuthContext = createContext({});

export function AuthProvider({children}) {
  const [isLogged, setIsLogged] = useState(false);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    // this function send the jwt token to backend to verify
    // if its valid, if so, it do a setIsLogged(true)
    async function checkToken() {
      const token = JSON.parse(localStorage.getItem('costs_token'));
      if (!token) return false;
      const headers = {
        'Content-Type': 'application/json',
        authorization: 'Bearer '+token
      }

      const response = await fetch('/auth/validatetoken', {
        method: 'GET',
        headers
      });

      if (response.status === 200) {
        setIsLogged(true);
        return true;
      }
      localStorage.removeItem('costs_token');
      return false;
    }

    setLoading(true);
    checkToken().then(() => setLoading(false));
  }, []);

  return (
    <AuthContext.Provider value={{isLogged, loading}}>
      {children}
    </AuthContext.Provider>
  );
}

I tried using a boolean value that do not come from AuthContext, it worked, but if the boolean come from the AuthContext, I can’t access it directly via url, it redirects to "/".

>Solution :

Start the app with an initially true loading state value so the PrivateRoute "waits" for the initial auth check to complete. When loading is initially false the PrivateRoute component fails the if-loading condition and conditionally renders the protected content or the redirect based on the passed condition prop. Since auth.isLogged is also initially false the Navigate component is rendered.

export function AuthProvider({ children }) {
  const [isLogged, setIsLogged] = useState(false);
  const [loading, setLoading] = useState(true); // <-- initially true

  useEffect(() => {
    // this function send the jwt token to backend to verify if its valid,
    // if so, it do a setIsLogged(true)
    async function checkToken() {
      const token = JSON.parse(localStorage.getItem('costs_token'));
      if (!token) return false;
      const headers = {
        'Content-Type': 'application/json',
        authorization: 'Bearer '+token
      }

      const response = await fetch('/auth/validatetoken', {
        method: 'GET',
        headers
      });

      if (response.status === 200) {
        setIsLogged(true);
        return true;
      }
      localStorage.removeItem('costs_token');
      return false;
    }

    setLoading(true);
    checkToken().then(() => setLoading(false));
  }, []);

  return (
    <AuthContext.Provider value={{isLogged, loading}}>
      {children}
    </AuthContext.Provider>
  );
}
function PrivateRoute({ condition, redirectRoute = '/login' }) {
  const auth = useAuth();
  
  // Wait until auth status confirmed/loaded
  if (auth.loading) return null;

  return condition
    ? <Outlet />
    : <Navigate to={redirectRoute} replace />;
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading