How to controling browser back button with react router dom v6?

Viewed 6489

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

4 Answers

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]);
};

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

window.onpopstate = () => {
  navigate("/");
}

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();
      }
    };
  }, []);

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;

Related