DEV Community

Cover image for Detecting user leaving page with react-router-dom v6.0.2

Detecting user leaving page with react-router-dom v6.0.2

Muhammad Bilal Bangash on January 23, 2022

Introduction: Before reading this article you should know about React Routing, its working. Main Focus of this post is detecting user l...
Collapse
 
mleister97 profile image
mleister97

Small bug:

Make sure to also set the lastLocation to null inside the method cancelNavigation.

Why?

Imagine:

  1. Filling out a form
  2. Switch page
  3. "Are you sure dialog" will open
  4. Cancel the dialog (stay on page)
  5. Update the form values again
  6. Submit the form via the default submit button
  7. Hook will keep the last location reminded and switch page now, even if not intended
Collapse
 
miguelpalacio profile image
Miguel Palacio

Thanks a lot Muhammad! 🙏🏼

This was extremely helpful. Worked like a charm.

Collapse
 
miguelpalacio profile image
Miguel Palacio

I had to modify useCallbackPrompt a little bit, as it didn't work properly when the component wasn't unmounted after a "confirmed navigation".

export function useCallbackPrompt(when) {
    const navigate = useNavigate();
    const location = useLocation();
    const [showPrompt, setShowPrompt] = useState(false);
    const [lastLocation, setLastLocation] = useState(null);
    const [confirmedNavigation, setConfirmedNavigation] = useState(false);

    const cancelNavigation = useCallback(() => {
        setShowPrompt(false);
    }, []);

    // handle blocking when user click on another route prompt will be shown
    const handleBlockedNavigation = useCallback(
        (nextLocation) => {
            // in if condition we are checking next location and current location are equals or not
            if (!confirmedNavigation && nextLocation.location.pathname !== location.pathname) {
                setShowPrompt(true);
                setLastLocation(nextLocation);
                return false;
            }
            return true;
        },
        // eslint-disable-next-line react-hooks/exhaustive-deps
        [confirmedNavigation, location]
    );

    const confirmNavigation = useCallback(() => {
        setShowPrompt(false);
        setConfirmedNavigation(true);
    }, []);

    useEffect(() => {
        if (confirmedNavigation && lastLocation) {
            navigate(lastLocation.location.pathname);

            // Clean-up state on confirmed navigation
            setConfirmedNavigation(false);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [confirmedNavigation, lastLocation]);

    useBlocker(handleBlockedNavigation, when);

    return [showPrompt, confirmNavigation, cancelNavigation];
}

Enter fullscreen mode Exit fullscreen mode

The changes are:

  1. [confirmedNavigation, location] - added location to the handleBlockedNavigation hook. The location object changes and the location.pathname value remains outdated.
  2. setConfirmedNavigation(false); - set after the programmatic navigation, state needed to be cleaned up so that future prompts are displayed.

Sorry if I'm not clear enough. I'm short of time and cannot elaborate a lot. Hopefully it will be useful for someone.

Thanks.

Collapse
 
bangash1996 profile image
Muhammad Bilal Bangash

Thank you Miguel Palacio for fixing :)

Collapse
 
yinkash profile image
Adebiyi Adeyinka

Using React Router Dom V6, there is no navigator.block() function. use custom history and history.block() function instead, as mentioned in Drew Reese answer on stackoverflow

Collapse
 
proddy profile image
Proddy

Thanks Muhammad for this post. I've been after a nifty solution for this.

One enhancement would be to check if the form value has actually changed, before triggering the dialog? This would mean comparing values in handleChange() and keeping the original form values/states. Any ideas how to implement this?

Collapse
 
mouerr profile image
Mouerr • Edited

Hi thanks a lot.
When i click in page reload button, it shows the default browser alertbox. any idea how to solve it?

Collapse
 
jackwhisler1 profile image
Jack Whisler

Having the same issue. Have you made any progress?

Collapse
 
maria_felecan_25d3b5692a0 profile image
Maria Felecan

Could this be work with some updates if we have a multi-page form? e.g. different pathnames for each step "/form/step1" and "/form/step2"?

Collapse
 
ismailbutt6926 profile image
IsmailButt6926

can u please give me a solution with react router dom version 6.8.1 ...this code is not working in my case becuase it gives an error that navigator.block is not a function ...can any one help me?

Collapse
 
mandarfokmare007 profile image
Mandar Fokmare

Facing same issue,Did you get any solution.?

Collapse
 
sumiyasayeed profile image
Sumiya Sayeed

I am getting the following error

Image description
Any idea how to solve it?

Collapse
 
pankajthakur950 profile image
Pankaj Thakur

can we have custom popup for browser refresh as well. I tried your code & got the browser default only. Is there a way to show custom message on browser reload?

Collapse
 
jaisaro14 profile image
Jai Saravanan

Could we have custom message in the browser prompt?

Collapse
 
bangash1996 profile image
Muhammad Bilal Bangash

I think it's normal to not be able to control everything, it looks like we are not able to add custom functionality to onbeforeunload. developer.chrome.com/blog/chrome-5...
@jaisaro14 @jackwhisler1 @mouerr