Credit goes to Steven Linn (github)
Note: This is only the solution to the FIRST part of the question. That is "How do you know when a nextjs 13 app (using the app router) is about to navigate?" I do not believe this solution can be used to prevent navigating away. Additionally, it does not work with the back and forward buttons. But a half solution is better than no solution so here I am.
I ran into this problem but for next 14.1.0. The way I was able to listen for a route change before it occurred was by referencing this github comment. They recommend using their nextjs13-router-events package.
But seeing I needed this for next 14.1.0, I took my own (similar) steps. In summary:
Copy paste the components' code from RouteChangeProvider
, Link
, and useRouteChange
.
Change all import Link from "next/link";
to import Link from "path/to/custom/Link";
Wrap your app with the RouteChangeProvider
import { RouteChangeProvider } from './RouteChangeProvider';
...
return (
<RouteChangeProvider>
{children}
</RouteChangeProvider>
)
- Use the hook in the components where you need to listen for route changes:
import useRouteChange from './useRouteChange';
...
export default function Component(props: any) {
...
useRouteChange({
onRouteChangeStart: () => {
console.log('Put code here to run BEFORE route changes');
},
onRouteChangeComplete: () => {
console.log('onComplete 3');
}
});
...
}
- (Additional step for next 14.1.0) In the
Link.tsx
component, move the const { onRouteChangeStart } = useRouteChangeContext();
line above the if (!useLink) return <a href={href} onClick={onClick} {...rest} />;
if (!useLink) return <a href={href} onClick={onClick} {...rest} />;
const { onRouteChangeStart } = useRouteChangeContext();
becomes
const { onRouteChangeStart } = useRouteChangeContext();
if (!useLink) return <a href={href} onClick={onClick} {...rest} />;
- (Additional step for next 14.1.0) In the
RouteChangeProvider.tsx
, add onRouteChangeComplete
to the dependency of the useEffect using it.
useEffect(() => onRouteChangeComplete(), [pathname, searchParams]);
becomes
useEffect(() => onRouteChangeComplete(),[pathname, searchParams, onRouteChangeComplete]);
P.S. The last two steps were a hotfix for an error and warning I received when creating the production build.