listen for router change via useEffect in NextJS

Viewed 10432

I am trying to get an useEffect to execute some code when a user changes the page via the navigation.

My problem is that the useEffect does not execute on the route change (does not show up in console). It does only show up on the first initial page load and never after

I used the google analytics example from nextJS

This is my simple _app.tsx

import type { AppProps } from "next/app";
import "./styles/css.scss";
import { useRouter } from "next/dist/client/router";
import { useEffect} from "react";



const Child: React.FC = ({ children }) => {
  return (
    <>
      <div>{children}</div>
    </>
  );
};

const App = ({ Component, pageProps }: AppProps) => {
  const router = useRouter();

  useEffect(() => {

    console.log("called");

  }, [router.events]);
  return (
    <>
      <Child>
        <Component {...pageProps} />
      </Child>
    </>
  );
};
export default App;
5 Answers

I was able to get this working more simply, by listening for changes to router.asPath.

  useEffect(() => {
    console.log('useEffect fired!', {asPath: router.asPath});
  }, [router.asPath]);

You can use routeChangeStart, routeChangeComplete and routeChangeError events to do some functionality whenever the route is changing.

Here I use NProgress to show slim progress bar, if you want you can try the same thing and get some idea about how its working or you can just log some data, () => { console.log("route change start") }

import '../styles/globals.css'
import 'nprogress/nprogress.css'
import NProgress from 'nprogress'
import { Router } from 'next/dist/client/router'
Router.events.on('routeChangeStart', () => {
  NProgress.start()
})
Router.events.on('routeChangeComplete', () => {
  NProgress.done()
})
Router.events.on('routeChangeError', () => {
  NProgress.done()
})
function MyApp({ Component, pageProps }) {
  return (
    <div>
      <h1 style={{ textAlign: 'center', alignItems: 'center' }}>
        Welcome to my App
      </h1>
      <Component {...pageProps} />
    </div>
  )
}

export default MyApp

Thank to @Faruk answer, I was able to find that it was possible but I had to use those router.events.on inside my hook.

useEffect(() => {
    router.events.on("routeChangeComplete", () => {
      console.log("route change routeChangeComplete");
    });
    return () => {
      router.events.off("routeChangeComplete", () => {
        console.log("stoped");
      });
    };
  }, [router.events]);
import  { useRouter } from "next/router";
import { useEffect, useState } from "react";

const App = ({ }) => {
  const router = useRouter(),
  [ready, setReady] = useState(false);

  const enableReady = async (loc) => {
  setReady(false);
  console.log("enableReady", loc);
};

const disableReady = async (loc) => {
  console.log(".....");
  await sleep(3);
  console.log("done");
  setReady(true);
  console.log("disableReady", loc);
};

useEffect(() => {
  router.events.on("routeChangeStart", async () => await enableReady("star a"));
  router.events.on("routeChangeComplete", async () => await disableReady("com a"));
  router.events.on("routeChangeError", async () => await disableReady("err a"));

  return () => {
    router.events.off("routeChangeStart", async () => await enableReady("star b"));
    router.events.off("routeChangeComplete", async () => await disableReady("com b"));
    router.events.off("routeChangeError", async () => await disableReady("err b"));
  };
}, [router.events]);

}

you can use useLocation for this

const App = ({ Component, pageProps }: AppProps) => {
  let location = useLocation();

  useEffect(() => {

    alert(location );// this will be called whenever the route changes 

  }, [location ]);
  return (
    <>
      <Child>
        <Component {...pageProps} />
      </Child>
    </>
  );
};
export default App;
Related