5

I looked around and tried to find a solution with React router. With V5 you can use <Promt />.
I tried also to find a vanilla JavaScript solution, but nothing worked for me.

I use React router v6 and histroy is replaced with const navigate = useNavigation() which doesn't have a .listen attribute.

Further v6 doesn't have a <Promt /> component.

Nevertheless, at the end I used useEffect clear function. But this works for all changes of component. Also when going forward.

According to the react.js docs, "React performs the cleanup when the component unmounts."

 useEffect(() => {
    // If user clicks the back button run function
    return resetValues();;
  })
Fotios Tsakiris
  • 1,310
  • 1
  • 18
  • 24
  • Are you wanting to run a function specifically when a back navigation occurs, or when ***any*** navigation away from the page occurs. These are two different scenarios. What have you tried already? What didn't work, what was the issue? Why doesn't the `Prompt` component work for you? https://stackoverflow.com/help/minimal-reproducible-example A more correct solution would be to create a history object and use [`history.listen`](https://github.com/remix-run/history/blob/main/docs/api-reference.md#historylistenlistener-listener) to listen for a POP action and handle accordingly. – Drew Reese Mar 08 '22 at 09:27
  • Hi @DrewReese you are right. I just realized that this works ofcourse on going forward too. Please check updated question to see the reasons why solutions don't work in react router v6. – Fotios Tsakiris Mar 08 '22 at 09:41

1 Answers1

2

Currently the Prompt component (and usePrompt and useBlocker) isn't supported in react-router-dom@6 but the maintainers appear to have every intention reintroducing it in the future.

If you are simply wanting to run a function when a back navigation (POP action) occurs then a possible solution is to create a custom hook for it using the exported NavigationContext.

Example:

import { UNSAFE_NavigationContext } from "react-router-dom";

const useBackListener = (callback) => {
  const navigator = useContext(UNSAFE_NavigationContext).navigator;

  useEffect(() => {
    const listener = ({ location, action }) => {
      console.log("listener", { location, action });
      if (action === "POP") {
        callback({ location, action });
      }
    };

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

Usage:

useBackListener(({ location }) =>
  console.log("Navigated Back", { location })
);

Edit how-to-run-a-function-when-user-clicks-the-back-button-in-react-js

If using the UNSAFE_NavigationContext context is something you'd prefer to avoid then the alternative is to create a custom route that can use a custom history object (i.e. from createBrowserHistory) and use the normal history.listen. See my answer here for details.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • 1
    Thanks a lot! Please elaborate a bit more. I use TypeScript and it throws an error on `navigator.listen(listener)`: `Property 'listen' does not exist on type 'Navigator'.` – Fotios Tsakiris Mar 08 '22 at 10:21
  • @FotiosTsakiris That's a good question that I don't have a great answer for since I'm not very familiar with TS. I see that `navigator` is [typed](https://github.com/remix-run/react-router/blob/main/packages/react-router/index.tsx#L65) but doesn't include `listen`. It works though because the `navigator` object ***is*** an internally created `history` object. See [BrowserRouter](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/index.tsx#L139-L169) for example. Perhaps you can create a union or extend the type that includes the `listen` function. – Drew Reese Mar 08 '22 at 10:29
  • 1
    No problem! I just saved the hook as normal JavaScript file `.js` and it works fine! Further, I get that `action: "POP"`. This is a great answer! Thanks again! – Fotios Tsakiris Mar 08 '22 at 10:52
  • As a temporary solution, you can do '(navigator as any).listen' – Jolly Good Jun 15 '22 at 16:00