19

I've been looking for this question and found it but they're using class components and react router dom v5

What i want is When user click browser back button I'll redirect them to home page

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Esa Kurniawan
  • 527
  • 1
  • 2
  • 15
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. – Community Mar 07 '22 at 03:40

7 Answers7

13

Well after a long journey to find out how to do that finally i came up with this solution

window.onpopstate = () => {
  navigate("/");
}
Esa Kurniawan
  • 527
  • 1
  • 2
  • 15
11

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:

import { useNavigate } from 'react-router-dom';
import { useBackListener } from '../path/to/useBackListener';

...

const navigate = useNavigate();

useBackListener(({ location }) =>
  console.log("Navigated Back", { location });
  navigate("/", { replace: true });
);

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.

Update w/ Typescript

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]);
};
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • With Typescript version I'm getting 'Cannot find module 'history' or its corresponding type declarations.' (react-router v6) – Eric Mar 02 '23 at 10:21
  • 1
    Also after installing _npm i history_ I've had navigator.listen is not a function error message – Eric Mar 02 '23 at 10:40
  • @Eric It seems the RRD maintainers are making it more and more difficult to implement use cases like these. If all you need is to listen for `POP` navigation actions then the [`useNavigationType`](https://reactrouter.com/en/main/hooks/use-navigation-type) hook might be useful. It might also be a bit easier than the above. I'll need to play around to see what might still work with the later RRDv6 versions after 6.4. – Drew Reese Mar 03 '23 at 10:02
  • My error: A history only accepts one active listener. I just put the `useBackListener` inside my component – tonisives Aug 31 '23 at 00:35
  • 1
    @tonisives Yeah, it gets rather sketchy/dicey after RRDv6.4 with the Data Routers. I'll need to revisit this answer later when I have time to see what still works in later versions. – Drew Reese Aug 31 '23 at 00:42
3

I came up with a pretty robust solution for this situation, just using browser methods, since react-router-v6's API is pretty sketchy in this department right now.

I push on some fake history identical to the current route (aka a buffer against the back button). Then, I listen for a popstate event (back button event) and fire whatever JS I need, which in my case unmounts the component. If the component unmounts WITHOUT the use of the back button, like by an onscreen button or other logic, we just clean up our fake history using useEffect's callback. Phew. So it looks like:

  function closeQuickView() {
    closeMe() // do whatever you need to close this component
  }

  useEffect(() => {
    // Add a fake history event so that the back button does nothing if pressed once
    window.history.pushState('fake-route', document.title, window.location.href);

    addEventListener('popstate', closeQuickView);

    // Here is the cleanup when this component unmounts
    return () => {
      removeEventListener('popstate', closeQuickView);
      // If we left without using the back button, aka by using a button on the page, we need to clear out that fake history event
      if (window.history.state === 'fake-route') {
        window.history.back();
      }
    };
  }, []);
light24bulbs
  • 2,871
  • 3
  • 24
  • 33
  • This works well, but has an issue that it can leave a ghost history entry. I'm have a *hamburger-menu* which contains links. If the *menu* is closed by cancelling it, it's fine. But if a **link** within the *menu* is clicked, by the time the *menu* unmounts, the **history.state** is no longer the `fake-route`. I'm going to see if I can add another listener then call `window.history.back()` and when that comes back, then navigate the user to their final destination. It feels like a lot of work for a simple thing, also I can't use simple links but rather have to redirect the user via JS. – MattWeiler Mar 27 '23 at 18:50
3

I used <Link to={-1}>go back</Link> and its working in v6, not sure if it's a bug or a feature but seems there is no error in console and can't find any documentation stating this kind of approach

John
  • 31
  • 2
0

You can go back by using useNavigate hook, that has become with rrd v6

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

const App = () => {

    const navigate = useNavigate();
    const goBack = () => navigate(-1);

    return (
        <div>
         ...
         <button onClick={goBack}>Go back</button>
         ...
        </div>
    )
}

export App;
0

You can try this approach. This worked for me.

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

const navigation = useContext(UNSAFE_NavigationContext).navigator;
const navigate = useNaviagte();

    React.useEffect(() => {
      let unlisten = navigation.listen((locationListener) => {
        if (locationListener.action === "POP") {
          //do your stuff on back button click
          navigate("/");
        }
      });
      return(() => {
        unlisten();
      })
    }, []);
-1

I'm on rrd@6.8 and testing John's answer worked for me right away for a simple "GO back 1 page", no useNavigate needed:

<Link to={-1}>
    <Button size="sm">← Back </Button>
</Link>

So as a simple back button this seems to work without unexpected errors.

  • this simply goes back 1 page. navigate(-1) does the same thing. The question is how to use the browsers back button to go back to a specific route. – Raziel Apr 04 '23 at 08:02