how to listen for route change in react-router-dom v6

Viewed 14586

am trying to migrate the old react router dom code to v6 and I want to know how to listen for route change, I am now using useHistory

const history = useHistory()
//then
history.listen(...)

I did read the new docs and I did find that useHistory was changed to useNavigate

const navigate = useNavigate()
//then
navigate.listen(...) // listen is not a function

can you please help me find a way to listen to the route change in v6

// This is a React Router v6 app
import { useNavigate } from "react-router-dom";

function App() {
  let navigate = useNavigate();
  function handleClick() {
    navigate("/home");
  }
  return (
    <div>
      <button onClick={handleClick}>go home</button>
    </div>
  );
}
3 Answers

The navigate function is a function, not an object like the older react-router-dom version 5's history object.

You can still create a custom history object but you'll need to create a custom router to use it. This allows you to import your history object and create listeners.

Create a custom router example, use one of the higher-level routers as an example for how they manage the location and state, i.e. BrowserRouter:

const CustomRouter = ({ history, ...props }) => {
  const [state, setState] = useState({
    action: history.action,
    location: history.location
  });

  useLayoutEffect(() => history.listen(setState), [history]);

  return (
    <Router
      {...props}
      location={state.location}
      navigationType={state.action}
      navigator={history}
    />
  );
};

In your code create the custom history object for use by your new custom router and other components. Ensure you have history@5 installed as a project dependency. This is the same version used by RRDv6. If you need to install it run npm i history@5 to add it to the project's dependencies.

const history = createBrowserHistory();
export default history;

Use your router and pass your history object to it.

import CustomRouter from '../CustomRouter';
import history from '../myHistory';

...

<CustomRouter history={history}>
  ....
</CustomRouter>

In a component you want to listen to location changes on, import your history object and invoke the listen callback as you did previously.

import history from '../myHistory';

...

useEffect(() => {
  const unlisten = history.listen((location, action) => {
    // ... logic
  });

  return unlisten;
}, []);

If you want, you may be able to also create your own custom useHistory hook that simply returns your history object.

Update

react-router-dom has started exporting a HistoryRouter for a use case like this. Instead of importing the low-level Router and implementing the internal logic you import unstable_HistoryRouter as HistoryRouter and pass your custom history object (memory, hash, etc).

import { unstable_HistoryRouter as HistoryRouter } from "react-router-dom";
import history from "../myHistory";

...

<HistoryRouter history={history}>
  ....
</HistoryRouter>

To add to the accepted answer (can't comment, not enough rep points), subscribing to the history through a useEffect with location.pathname in the dependency array won't work if the navigation unmounts the component you're attempting to call the useEffect from.

Related