Reputation: 1895
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;
Upvotes: 25
Views: 27326
Reputation: 20231
As of latest NextJS 14, You can listen for page changes by composing other Client Component hooks like usePathname and useSearchParams.
'use client'
import { useEffect } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'
export function NavigationEvents() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
const url = `${pathname}?${searchParams}`
console.log(url)
// You can now use the current URL
// ...
}, [pathname, searchParams])
return null
}
And wrap this component with Suspense
import { Suspense } from 'react'
import { NavigationEvents } from './components/navigation-events'
export default function Layout({ children }) {
return (
<html lang="en">
<body>
{children}
// Wrap the component with Suspense here
<Suspense fallback={null}>
<NavigationEvents />
</Suspense>
</body>
</html>
)
}
source: https://nextjs.org/docs/app/api-reference/functions/use-router#examples
Upvotes: 1
Reputation: 8536
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]);
}
Upvotes: 2
Reputation: 475
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]);
Upvotes: 21
Reputation: 111
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
Upvotes: 7
Reputation: 1895
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]);
Upvotes: 11
Reputation: 1764
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;
Upvotes: 0