0

In my project I use React Router DOM 5.2.0. I have to use <Router /> with custom history due to project needs. And this is how it goes:

Example on Sandbox

history.js

import { createBrowserHistory } from 'history';

const history = createBrowserHistory();

export default history;

useRouter.js

import {
  useParams,
  useRouteMatch,
} from 'react-router-dom';
import { useMemo } from 'react';

import history from './history';
import urlQueriesService from './urlQueriesService';

const useRouter = () => {
  const params = useParams();
  const match = useRouteMatch();
  const { location } = history;

  return useMemo(() => ({
    push: history.push,
    replace: history.replace,
    pathname: location?.pathname,

    query: {
      ...urlQueriesService.parse(location?.search),
      ...params,
    },

    match,
    location,
    history,
  }), [params, match, location, history]);
};

export default useRouter;

AppRoutes.jsx

import React from 'react';
import {
  Route,
  Switch,
} from 'react-router-dom';

import useRouter from './useRouter';
import TestPage from './TestPage';
import TestComponent from './TestComponent';

const AppRoutes = () => {
  const { location } = useRouter();
  return (
    <Switch location={location}>
      <Route exact path="/page-1" component={Page1} />
      <Route exact path="/page-2" component={Page2} />
    </Switch>
  );
};

export default AppRoutes;

App.jsx

import history from './history';

<Router history={history}>
  <AppRoutes />
</Router>

This is the basic setup. Page1 and Page2 are cross linking components and look like this:

import React from 'react';
import { Link } from 'react-router-dom';

import useRouter from './useRouter';

const Page1 = () => {
  const { location } = useRouter();

  return (
    <div>
      <h1>Test</h1>

      <div>
        <Link to={{ pathname: '/page-2', state: { prevUrl: location.pathname }}}>Stateful component</Link>
      </div>
    </div>
  );
};

export default Page1;

The key part here is Link component which has to prop with object as value. In this object I define state: { prevUrl: location.pathname }, but this state is always null when I try to access it in Page2. The fun thing here is that if I change <Router /> to be BrowserRouter and remove custom location from Switch - things work very well, meaning <Link /> does propagate the state. In my case, however, it looks like state is not being handled by the history at all.

System:

  • Mac OS 11.4 Big Sur
  • Node 12.16.2
  • NPM 6.14.11
  • React 16.13.1
  • react-router-dom 5.2.0
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Why aren't you using the `useLocation` hook for the `location` in your `useRouter` custom hook? Why are you trying to use a *different* location on the `Switch` component? You can use `Router` with a `createBrowserHistory` `history` object without issue, I think it's the odd `location` object used on the `Switch` messing it up for you. – Drew Reese May 28 '21 at 10:29
  • Seems like `Switch` needs to know the `location` object is used to correctly handle component updates. If I remove one - it doesn't render new component. In `useRouter` I use `location` from `history` object that I created to provide to `Router`, that's why I don't use `useLocation`, because it'd return me location from different object. I created a sandbox to make it easier to click through https://codesandbox.io/embed/dazzling-sunset-cn4de?autoresize=1&fontsize=14&hidenavigation=1&theme=dark – Aliaksandr Savitski May 31 '21 at 07:24

1 Answers1

0

I just found an answer thanks to this post https://stackoverflow.com/a/62583930/8080990

Downgrade to history version 4, and it works perfect!

Sandbox