-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Which project does this relate to?
Router
Describe the bug
There is a bug with the useBlocker hook where under specific conditions, both a custom UI blocker AND the browser's native beforeunload confirmation dialog appear in sequence. The custom UI blocker appears first, and then after calling proceed, the browser's native confirmation dialog also appears.
The issue occurs when all of the following conditions are met:
withResolveris set totrue(to enable the custom UI)- Both
enableBeforeUnloadandshouldBlockFnreturntrueuntil the hook is fully unmounted (in our case, based on form dirtiness detection) - In the
beforeLoadof the destination route, there is athrow redirectto an external site
Additionally, passing ignoreBlocker: true to the redirect function that is thrown does not prevent the browser confirmation dialog from showing.
Your Example Website or App
Steps to Reproduce the Bug or Issue
-
Create a component that uses
useBlockerwith:// In src/routes/index.tsx const { status, proceed, reset } = useBlocker({ shouldBlockFn: () => true, enableBeforeUnload: () => true, withResolver: true, });
-
Add a route with a
beforeLoadfunction that contains a redirect to an external site:// In src/routes/external.tsx export const Route = createFileRoute('/external')({ component: External, beforeLoad: () => { throw redirect({ href: 'https://example.com/' }); }, });
-
Try to navigate to the '/external' route (you can add a link to it on the index page)
Expected behavior
When navigating to a route that redirects externally, only the custom UI blocker should appear. Once the user confirms by clicking "proceed" in the custom UI, the navigation should continue without triggering the browser's native beforeunload confirmation dialog.
Screenshots or Videos
No response
Platform
- OS: macOS
- Browser: Chrome
- Version: 1.119.0
Additional context
I found a workaround by modifying the enableBeforeUnload function to include a check against the router state:
enableBeforeUnload: () => formIsDirty && router.state.status !== "pending"This prevents the browser confirmation dialog from appearing when the router status is "pending", which occurs during the redirect. However, this seems like a workaround for what appears to be a bug in the interaction between useBlocker, beforeunload events, and redirects to external sites.
The key reproduction components are:
- A route using
useBlockerwith bothshouldBlockFnandenableBeforeUnloadreturning true - A separate route with a
beforeLoadfunction that throws an external redirect - When navigating to the external route, first the custom UI blocking appears, and after clicking "proceed", a browser confirmation dialog also appears
It would be helpful if there was a more official way to handle this case, perhaps by having ignoreBlocker: true properly bypass all forms of blocking, including the beforeunload event.