How can I re-run the parent route code when I'm switching between the nested children routes

Viewed 21

Take a look at my protected route for unauthenticated pages:

import React, { useEffect } from 'react'
import { Route, Outlet, Navigate } from 'react-router-dom'
import { Login, SignUp } from '../pages'
import useVerifyToken from '../hooks/useVerifyToken'

const InauthenticatedRoutes = () => {

    const isAuth = useVerifyToken()

    return !isAuth ? <Outlet /> : <Navigate to="/"/>
}

const routes = [
    <Route key="3234" path='/' element={<InauthenticatedRoutes />}>
        <Route key="6757" path='login' element={<Login />}/>
        <Route key="67567" path='signup' element={<SignUp />}/>
    </Route>
]

export default routes

The reason this file exported an array is because this will later be iterated inside the Routes element

the useVerifyToken is something like this

function useVerifyToken() {
  const jwt = localStorage.getItem('access')

  let data = 0

  useEffect(() => {

    ....verifying token

  }, [window.location.href])

  return !data ? 0 : 1;
}

As you can see inside the useVerifyToken hook there's a useEffect that has a window.location.href as a dependency this is because I want this hook to verify the token every time the user switch in between pages. But the problem here it's that if I went from the route Login to Signup which is nested under the same parent, the useVerifyToken wouldn't be triggered. Personally I can't really find any work around without radically changing the route structure.

My question

How do I trigger the useVerifyToken to run even if I have to access a route under the same parent route? if not then what kind of work around would you recommend

1 Answers

I would suggest a bit of a rewrite of the useVerifyToken to have a loading state and the isAuth state. Return both values from the hook such that you can conditionally render some alternative UI while the token is being verified. Use the useLocation hook to access the location.pathname value to be used as a useEffect hook dependency. When the pathname changes the effect callback will run.

Example:

import { useLocation } from 'react-router-dom';

const useVerifyToken = () => {
  const { pathname } = useLocation();

  const [isAuth, setIsAuth] = React.useState();
  const [isLoading, setIsLoading] = React.useState(true);

  useEffect(() => {
    const verifyToken = async () => {
      const token = localStorage.getItem('access');
      setIsLoading(true);
      try {
        ....verifying token
        setIsAuth(/* validation result, true|false */);
      } catch(error) {
        // handle error, log, set any error state, etc...
        setIsAuth(false);
      } finally {
        setIsLoading(false);
      }
    };
    
    verifyToken();
  }, [pathname])

  return { isAuth, isLoading };
};

Update InauthenticatedRoutes to use both states returned from the hook:

const InauthenticatedRoutes = () => {
  const { isAuth, isLoading } = useVerifyToken();

  if (isLoading) {
    return null; // <-- or loading indicator, spinner, etc...
  }

  return !isAuth ? <Outlet /> : <Navigate to="/" replace />;
}

Now just render the routes normally, no need for them to be in any array really.

<Routes>
  ...
  <Route element={<InauthenticatedRoutes />}>
    <Route path='login' element={<Login />} />
    <Route path='signup' element={<SignUp />} />
    ... other anonymous routes
  </Route>
  ...
</Routes>
Related