16

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.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
김정수
  • 611
  • 4
  • 9
  • 22

5 Answers5

19

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.

maxeth
  • 1,315
  • 1
  • 8
  • 17
3

I also encountered a similar problem. An error will be reported when the network is slow or the push event is triggered multiple times in a short period.

I solved it like this:

import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';

const useSafePush = () => {
  const [onChanging, setOnChanging] = useState(false);
  const handleRouteChange = () => {
    setOnChanging(false);
  };
  const router = useRouter();
  // safePush is used to avoid route pushing errors when users click multiple times or when the network is slow:  "Error: Abort fetching component for route"
  const safePush = (path: string) => {
    if (onChanging) {
      return;
    }
    setOnChanging(true);
    router.push(path);
  };

  useEffect(() => {
    router.events.on('routeChangeComplete', handleRouteChange);

    return () => {
      router.events.off('routeChangeComplete', handleRouteChange);
    };
  }, [router, setOnChanging]);
  return { safePush };
};

export default useSafePush;

I observe the routeChangeComplete event, only when the route change is complete, the push function will be called.

usage:

  const { safePush } = useRouterChange();
  safePush(path)
林志煌
  • 39
  • 1
1

In my case I have use rotuer.push("/") two times in a single file. That caused the error. Try using one. I think problem will be solved.

enter image description here

  • 1
    you shouldn't use side effects inside render body – move call `router.push` inside `useEffect` – akaSybe Jan 28 '23 at 04:59
0

This error occurs because useEffect tries to update a component that has already been unmounted and this can introduce memory leaks in which your app uses more memory than it needs to. To prevent this, use the following approach:


  useEffect(() => {
  //first manually mount the effect
   let mounted = true;

  //check if component is currently mounted
   if(mounted && ...code){
     router.push('/index')
     }
  //cleanup side effects before unmounting
    return()=>{mounted=false}
  }, [router]);
};
-3

In my current NextJS project, when I make reactStrictMode: false,, the value to false, then it seems like the re-rendering will be gone, and component will be only rendered once.

I don't like react strict mode very much ..

DamonWu
  • 108
  • 3