authenticationnext.jsnext-router

Next.JS Abort fetching component for route: "/login"


I was developing a useUser Hook for per-page authentication. I have implemented the useUser hook normally and Redirecting works fine accordingly. But I am getting the above error.

Abort fetching component for route: "/login"

How can I fix useUserHook to solve it??

//useUser.tsx
const useUser = ({ redirectTo, redirectIfFound }: IParams) => {
  const { data, error } = useRequest("authed", isAuthed);

  const user = data?.data;
  const hasUser = user;

  useEffect(() => {
    if (!redirectTo) return;
    if (
      // If redirectTo is set, redirect if the user was not found.
      (redirectTo && !redirectIfFound && !hasUser) ||
      // If redirectIfFound is also set, redirect if the user was found
      (redirectIfFound && hasUser)
    ) {
      Router.push(redirectTo);
    }
  }, [redirectTo, redirectIfFound, hasUser]);

  return error ? null : user;
};
//index.tsx
const Home: NextPage = () => {
  const user = useUser({ redirectTo: "/login" });

  if (user === undefined || user === false) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <Head>
        
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <div>Home</div>
    </div>
  );
};

UseRequest Hook returns true and false as return values.


Solution

  • tl;dr

    Ensure that you only call router.push() once throughout all potential re-executions of useEffect with the help of state:

    
    const [calledPush, setCalledPush] = useState(false); // <- add this state
    
    // rest of your code [...]
    
     useEffect(() => {
        if (!redirectTo) return;
        if (
          (redirectTo && !redirectIfFound && !hasUser) ||
          (redirectIfFound && hasUser)
        ) {
            // check if we have previously called router.push() before redirecting
          if (calledPush) {
            return; // no need to call router.push() again
          }
    
          Router.push(redirectTo);
          setCalledPush(true); // <-- toggle 'true' after first redirect
        }
      }, [redirectTo, redirectIfFound, hasUser]);
    
      return error ? null : user;
    };
    
    

    Background

    useEffect potentially gets called multiple times if you have more than one dependency (Also happens with React Strict Mode enabled, but in this case there seems to be no error), and (re-)calling router.push() multiple times within the same Next.js page in different places/throughout different re-renders seems to cause this error in some cases, as the redundant router.push() call(s) will have to be aborted, because the current page-component unmounts due to the successful, previously called router.push().

    If we keep track of whether we have already called router.push via the calledPush state as in the code snippet above, we omit all redundant router.push() calls in potential useEffect re-executions, because for all subsequent useEffect executions the state value calledPush will already be updated to true as useEffect gets triggered after re-renders, hence after setCalledPush(true) takes effect.