Before Unload event BUT .... for the app router
Answered
Giant panda posted this in #help-forum
Giant pandaOP
Hi folks,
Just wanted to know how do you handle "before unload" event with the app router ?
At the moment, when use
But for the internal routing of an app made with Next, we have no way to detect if the route IS GOING to change and block the router event
What hacks do you use to make sure that before the user change the current page internally (with Link for example) ?
Thanks!
Just wanted to know how do you handle "before unload" event with the app router ?
At the moment, when use
window.addEventListener("beforeUnload") it will triggers the browser popup when the user wants to close the tab or try to refresh the current tab (which is correct)But for the internal routing of an app made with Next, we have no way to detect if the route IS GOING to change and block the router event
What hacks do you use to make sure that before the user change the current page internally (with Link for example) ?
Thanks!
Answered by Giant panda
Ok I think I have found a solution.
My solution was to create a component that would replace my all the currents
The idea is that as soon as one of my forms is considered dirty, a parameter is added to the url.
For example
The new
The implementation is very basic for this solution, so if you've got another way, I'd be happy to take it, but for the time being I'll go with this and I have just replaced all the imports from
My solution was to create a component that would replace my all the currents
<Link /> components to override it.The idea is that as soon as one of my forms is considered dirty, a parameter is added to the url.
For example
hasUnsavedChanges=true. const form = useForm() // An example with React Hook Form
const router = useRouter();
useEffect(() => {
if (!form.formState.isDirty) return; //
router.push("?hasUnsavedChanges=true");
}, [form.formState.isDirty]);The new
<Link/> component monitors the parameter in the URL using the useSearchParams hook, and depending on this boolean will allow the link to continue its routing or not."use client";
type Props = ComponentProps<typeof Link>;
const LinkWithBeforeUnload = (props: Props) => {
const searchParams = useSearchParams();
const hasUnsavedChanges = searchParams.get("hasUnsavedChanges") === "true";
const onClick = (e: MouseEvent<HTMLAnchorElement, globalThis.MouseEvent>) => {
if (!hasUnsavedChanges) {
return;
}
const shouldContinue = window.confirm(
"You have unsaved changes, are you sure you want to leave this page?"
);
if (!shouldContinue) {
e.preventDefault();
}
};
return (
<Link {...props} onClick={onClick}>
{props.children}
</Link>
);
};
export default LinkWithBeforeUnload;The implementation is very basic for this solution, so if you've got another way, I'd be happy to take it, but for the time being I'll go with this and I have just replaced all the imports from
next/link to the current path of LinkWithBeforeUnloadâš ï¸ It's a client component only don't forget to add use client; directive1 Reply
Giant pandaOP
Ok I think I have found a solution.
My solution was to create a component that would replace my all the currents
The idea is that as soon as one of my forms is considered dirty, a parameter is added to the url.
For example
The new
The implementation is very basic for this solution, so if you've got another way, I'd be happy to take it, but for the time being I'll go with this and I have just replaced all the imports from
My solution was to create a component that would replace my all the currents
<Link /> components to override it.The idea is that as soon as one of my forms is considered dirty, a parameter is added to the url.
For example
hasUnsavedChanges=true. const form = useForm() // An example with React Hook Form
const router = useRouter();
useEffect(() => {
if (!form.formState.isDirty) return; //
router.push("?hasUnsavedChanges=true");
}, [form.formState.isDirty]);The new
<Link/> component monitors the parameter in the URL using the useSearchParams hook, and depending on this boolean will allow the link to continue its routing or not."use client";
type Props = ComponentProps<typeof Link>;
const LinkWithBeforeUnload = (props: Props) => {
const searchParams = useSearchParams();
const hasUnsavedChanges = searchParams.get("hasUnsavedChanges") === "true";
const onClick = (e: MouseEvent<HTMLAnchorElement, globalThis.MouseEvent>) => {
if (!hasUnsavedChanges) {
return;
}
const shouldContinue = window.confirm(
"You have unsaved changes, are you sure you want to leave this page?"
);
if (!shouldContinue) {
e.preventDefault();
}
};
return (
<Link {...props} onClick={onClick}>
{props.children}
</Link>
);
};
export default LinkWithBeforeUnload;The implementation is very basic for this solution, so if you've got another way, I'd be happy to take it, but for the time being I'll go with this and I have just replaced all the imports from
next/link to the current path of LinkWithBeforeUnloadâš ï¸ It's a client component only don't forget to add use client; directiveAnswer