skip to Main Content

Every page goes through an AuthGuard component which determines if child components can be shown or if the user needs to be redirected to another page.

It works well when first visiting a page, but I’ve noticed some weird behavior when going back to the previous page. For example:

  1. I’m not logged in and go to /home, which is only shown for logged in users. I get redirected to /login, which is correct.
  2. I click in my browser (top left) to go to the previous page. Note that I don’t go to the previous page by clicking on a link on the website.
  3. For less than a second, I can see the content of /home, then I get redirected back to /login, which is handled by AuthGuard.

I use a boolean state hook for showing children and noticed that it’s still set to true for a tiny bit when going back to the previous page. true is still the old value it got when loading /login. Why doesn’t it start afresh?

Here’s my AuthGuard:

const AuthGuard = ({ children, restriction }) => {
  const [canShowChildren, setCanShowChildren] = useState<boolean>(false);
  const { user, loading } = useAuth();
  const router = useRouter();

  // I need to use useEffect & async because some user data can only be retrieved
  // asynchronously, though I haven't included that part here because it's 
  // irrelevant to the problem.
  useEffect(() => {
    (async () => {
      if (loading || !router.isReady) return;

      if (restriction === "public") {
        setCanShowChildren(true);
        return;
      }

      if (!user) {
        router.push("/login");
        return;
      }

      ...
    })();
  }, [loading, restriction, router, user]);

  if (loading || !router.isReady) {
    return <Loading />;
  }

  return canShowChildren ? children : <Loading />;

}

I set the restriction in a page through getStaticProps, like:

export async function getStaticProps() {
  return {
    props: {
      restriction: "public",
    },
  };
}

2

Answers


  1. Chosen as BEST ANSWER

    I was able to get it working by also saving the route in a state hook. However, I see it as a dirty fix and would love to know more about why is this happening in the first place.

    const AuthGuard = ({ children, restriction }) => {
      const [canShowChildren, setCanShowChildren] = useState<boolean>(false);
      const { user, loading } = useAuth();
      const router = useRouter();
      const [route, setRoute] = useState<string>("");
    
      useEffect(() => {
        (async () => {
          if (router.asPath !== route) setCanShowChildren(false);
          setRoute(router.asPath);
          ...
        })();
      }, [loading, restriction, router, user, pageRoute]);
    
      
      if (loading || !router.isReady || router.asPath !== route) {
        return <Loading />;
      }
      
      ...
    };
    

  2. a simple solution can achieve this is by watching the route in useEffect and whenever it changes clear or reset the state

    import {useRouter} from ‘next/router’

    const Page = (props) => {

    const [state, setState] = useState(someState)

    const pageRoute = useRouter().asPath

    useEffect(() => {
    setState(resetState) // When the page route changes the state will reset.
    }, [pageRoute])

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search