Search code examples
javascriptreactjsauthenticationreact-routeraws-amplify

Prevent falsy request from React Router loader function


I use React Router 6.4.2 and it's API - createBrowserRouter.
I have a default request that should be done only once when user reach main route ('/').
Request requires auth token (Amplify) so i use protected routes. No token - redirect to './auth/.

Router:

const router = createBrowserRouter([
  {
    element: (
      <RequireAuth>
        <AppLayout />
      </RequireAuth>
    ),
    children: [
      {
        path: '/',
        loader: getDefaultData,
        element: <MainPage />,
      },
    ],
  },
  { path: '/auth', element: <Authenticator /> },
  {
    path: '*',
    element: <NotFoundPage />,
  },
]);

export const AppRouter = () => {
  return <RouterProvider router={router} fallbackElement={<AppLoader centered />} />;
};

RequireAuth:

export const RequireAuth = ({ children }: Props) => {
  const location = useLocation();
  const { route } = useAuthenticator((context) => [context.route]);
  if (route !== 'authenticated') {
    return <Navigate to="/auth" state={{ from: location }} replace />;
  }
  return <>{children}</>;
};

GetDefaultData:

export const getDefaultData = async () => {
  store.dispatch(
    getData({
      someVariables,
    })
  );
};

What i faced: when not authenticated user try to reach main route ('/'), he reach it for a moment before he will be redirected to './auth/ and React Router run getDefaultData from loader that fails on getting-auth-toker step.

What i expect: React Router will skip getDefaultData for that case. Looking for a way how to tell React Router about that in a beautiful way.

P.S. I know that i can add auth check with return inside getDefaultData function (generally it happens but not from scratch but on getting-auth-token).
I know about shouldRevalidate but not sure that it can help me in that case.

UPD. provided a codesandbox for that https://codesandbox.io/s/amazing-matsumoto-cdbtow?file=/src/index.tsx Simply try remove '/auth' from url manually and check console.
UPD. created an issue about that https://github.com/remix-run/react-router/issues/9529


Solution

  • Got an answer from Matt Brophy in github:
    Fetching is decoupled from rendering in 6.4, so loaders run before any rendering logic. You should lift your "requires authorization" logic out of the RequireAuth component and into your loaders and redirect from there. Redirecting during render is too late 😄 Also note that all loaders for a matched route route run in parallel, so you should check in each loader for now (just like in Remix). We plan to make this easier to do in one place in the future.
    Original answer: https://github.com/remix-run/react-router/issues/9529