use NextRouter outside of React component

Viewed 8859

I have a custom hook that will check whether you are logged in, and redirect you to the login page if you are not. Here is a pseudo implementation of my hook that assumes that you are not logged in:

import { useRouter } from 'next/router';

export default function useAuthentication() {

  if (!AuthenticationStore.isLoggedIn()) {
    const router = useRouter();
    router.push('/login'); 
  }
}

But when I use this hook, I get the following error:

Error: No router instance found. you should only use "next/router" inside the client side of your app. https://err.sh/vercel/next.js/no-router-instance

I checked the link in the error, but this is not really helpful because it just tells me to move the push statement to my render function.

I also tried this:

// My functional component
export default function SomeComponent() {

  const router = useRouter();
  useAuthentication(router);

  return <>...</>
}

// My custom hook
export default function useAuthentication(router) {

  if (!AuthenticationStore.isLoggedIn()) {
    router.push('/login');
  }
}

But this just results in the same error.

Is there any way to allow routing outside of React components in Next.js?

3 Answers

The error happens because router.push is getting called on the server during SSR on the page's first load. A possible workaround would be to extend your custom hook to call router.push inside a useEffect's callback, ensuring the action only happens on the client.

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

export default function useAuthentication() {
    const router = useRouter();

    useEffect(() => {
        if (!AuthenticationStore.isLoggedIn()) {
            router.push('/login'); 
        }
    }, []);
}

Then use it in your component:

import useAuthentication from '../hooks/use-authentication' // Replace with your path to the hook

export default function SomeComponent() {
    useAuthentication();

    return <>...</>;
}

create a HOC which will wrap your page component

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

export default function UseAuthentication() {
 return () => {
    const router = useRouter();

    useEffect(() => {
      if (!AuthenticationStore.isLoggedIn()) router.push("/login");
    }, []); 
// yous should also add isLoggedIn in array of dependancy if the value is not a function

    return <Component {...arguments} />;
  };
}

main component

function SomeComponent() {


  return <>...</>
}
export default UseAuthentication(SomeComponent)
Related