0

I have "react-dom-router v6.3.0" (strictly!) now and I couldn't understand how to handle browser's "back" button. For example I need to catch the event, so I could open warning modal that user leaves the page after clicking back. At least give me a direction, please.

I'm using Typescript 4.4.2.

The useBackListener:

import { useEffect, useContext } from "react";
import { NavigationType, UNSAFE_NavigationContext } from "react-router-dom";
import { History, Update } from "history";

const useBackListener = (callback: (...args: any) => void) => {
  const navigator = useContext(UNSAFE_NavigationContext).navigator as History;

  useEffect(() => {
    const listener = ({ location, action }: Update) => {
      console.log("listener", { location, action });
      if (action === NavigationType.Pop) {
        callback({ location, action });
      }
    };

    const unlisten = navigator.listen(listener);
    return unlisten;
  }, [callback, navigator]);
};

Then usage:

  useBackListener(({ location }) => {
    if (isDirty) {
      setOpenWarning(true)
    } else navigate("go back")
  })

How to open modal if form is dirty without redirecting after clicking browser's "back" button ? Also, is it possible to avoid @ts-ignore?

niar6
  • 1
  • 1
  • 2
  • You can still listen to navigation changes. Do either of [this](/a/71399121/8690857) or [this](/q/36355093/8690857) help? Hopefully you are not trying to keep users stuck in your app. What have you tried? What is any issue? See [mcve]. – Drew Reese Aug 19 '22 at 21:26
  • All these are first of all what I found. It is not suitable for this version 6.3.0. For example, "Binding element 'location' implicitly has an 'any' type". Navigation.listen is removed judging changelog version. – niar6 Aug 19 '22 at 21:36
  • Oh, is Typescript important for your question/issue? Also, both of these solutions work with RRDv6. The first is *specifically* for v6. – Drew Reese Aug 19 '22 at 21:37
  • Yes, Typescript is important for my project. – niar6 Aug 19 '22 at 21:39
  • Ok, we can tag the question with typescript. In the meantime, is there anything you've already tried that you can share with us? – Drew Reese Aug 19 '22 at 21:42
  • FWIW the `navigator` still *technically* has a `listen` property, but RRDv6 omits it when exporting the `Navigator` type [here](https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/context.ts#L16). This is why it "just works" in non-TS projects. In other words, it's still just the `history` object exported from `history.js` and is just a typing issue at this point. I have no idea why they (*the maintainers*) have done it this way (*though I suspect you could dig through commit history or check their github issues*). – Drew Reese Aug 19 '22 at 21:58
  • I've updated my answer [here](/a/71399121/8690857) with a Typescript version, and here's a running [codesandbox](https://codesandbox.io/s/how-to-handle-the-browsers-back-button-in-react-1hkrvl). There's not any decent way to prevent back navigations away from a page as this is something most browser maintainers are actively trying to prevent you from doing. The best you can probably do is something like listening to the `onbeforeunload` event when anything on the page is dirty to "alert" the user they may have unfinished business. – Drew Reese Aug 20 '22 at 00:35
  • We should show a warning modal after pressing only the browser "back" button and nothing else. So I assume that onbeforeunload is not the right way to do it. Or I may have misunderstood. Thanks for your adaption for Typescript. – niar6 Aug 20 '22 at 13:27
  • Well, basically I'm trying to tell you that *by the time* you can detect the back button's been pressed, it's too late, the button will have been pressed and the navigation effected. This is why I mention the `onbeforeunload` event where you can handle cleaning up resources or saving some state prior to navigating away from the page ***or*** trigger the continue/cancel modal which is effectively the one chance you have to cancelling the navigation. – Drew Reese Aug 20 '22 at 23:34
  • I'm a little confused. In my case, I can call only the browser modal window, not the modal window of my component. I've tried to replace. And not to mention the fact that `unbeforeunload` doesn't trigger to the back button (does the useBackListener help, if yes then how to tweak one another?). How to save state/cleaning up I don't get at least for now. I'm using `@types/react-beforeunload`. I'll handle it, there is a direction. – niar6 Aug 21 '22 at 04:14
  • Okay, nothing helped. Can you give some more hints, please? – niar6 Aug 22 '22 at 11:44
  • Sometimes you need to apply several things to handle all edge cases. For example, event listeners that listen for POP actions from the browser's back button, the event listeners for the `onbeforeunload` event to handle page reloads or the component unmounting while there is "dirty state" to possibly handle, etc. – Drew Reese Aug 22 '22 at 17:04

2 Answers2

0

You can create your own Custom Router with history object which will help you to listen actions such as "POP" for react-router-dom v6.

To create custom route you may want to follow these steps: https://stackoverflow.com/a/70646548/13943685

mfturkcan
  • 41
  • 1
  • 7
-1

This how React Router specific history object comes into play. It provides a way to "listen for URL" changes whether the history action is push, pop, or replace

 let history = createBrowserHistory();
history.listen(({ location, action }) => {
  // this is called whenever new locations come in
  // the action is POP, PUSH, or REPLACE
});

OR you can also use

window.addEventListener("popstate", () => {
  // URL changed!
});

But that only fires when the user clicks the back or forward buttons. There is no event for when the programmer called window.history.pushState or window.history.replaceState.