17

Right now I have an History module, that let me use the history even outside of react component:

import { createBrowserHistory } from 'history';

export default createBrowserHistory();

Then I use this history in App.jsx directly

import { Router } from 'react-router-dom';
...
<Router history={history}>...</Router>

I can import and use the same history object everywhere. Even in custom helpers outside of any react components.

How could this work in react router 6?

Since history is replaced with navigate, I don't see any solution yet online.

I know it is still beta, but I would like to check on it in advance.

Thanks for any ideas!

bukso
  • 1,108
  • 12
  • 23

4 Answers4

17

I ended up with following solutions:

At first, react-router-dom 6 has navigate and not history. It is better to use navigate for the navigation trough the routes:

I create and fix my History object, so that it can continue work with 'push' and that I don't need big rework:

const History = {
  navigate: null,
  push: (page, ...rest) => History.navigate(page, ...rest),
};

export default History;

Then I made my own Component that set the navigation:

import { useNavigate } from 'react-router-dom';

const NavigateSetter = () => {
  History.navigate = useNavigate();

  return null;
};

Then while defining the default router, I place as a child the setter:

import { BrowserRouter } from 'react-router-dom';

<BrowserRouter>
    <NavigateSetter />
    <App />
</BrowserRouter>

After that you can use everywhere History.push or History.navigate with the default react-router-dom's navigate API.

bukso
  • 1,108
  • 12
  • 23
7

Inspired by @bukso, I ended up with the following solution in TypeScript.

global-history.tsx

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

export let globalNavigate: NavigateFunction;

export const GlobalHistory = () => {
  globalNavigate = useNavigate();

  return null;
};

index.tsx

<React.StrictMode>
  <BrowserRouter>
    <GlobalHistory />
    {/* ... */}
  </BrowserRouter>
</React.StrictMode>

Now, you're ready to use it outside React components. globalNavigate("profile")

Luka
  • 828
  • 9
  • 15
0

In my case I needed access to the history object before first render, so I ended up writing custom component around Router based on the BrowserRouter implementation that would allow me to pass in history through props.

See https://github.com/remix-run/react-router/discussions/8241#discussioncomment-1677474 for code.

Edit: As of react-router-dom v6.1.0 HistoryRouter is part of its implementation so you can just directly do:

import {unstable_HistoryRouter as HistoryRouter} from 'react-router-dom'
import {createBrowserHistory} from 'history'

const history = createBrowserHistory()

<HistoryRouter history={history} />
Martin Kadlec
  • 4,702
  • 2
  • 20
  • 33
  • 1
    I am not quite sure if this is good solution. The react-router team explicitly move from history to navigate with the goal of "better compatibility with React suspense" https://reactrouter.com/docs/en/v6/upgrading/v5#use-usenavigate-instead-of-usehistory – bukso Dec 14 '21 at 08:56
  • @bukso As I wrote in the edit, they added it themselves to the library in 6.1 – Martin Kadlec Dec 14 '21 at 12:05
  • Kadlek, okay, but it is still unstable - https://github.com/remix-run/react-router/releases – bukso Dec 14 '21 at 12:47
0

Here is my solution, I wanted to navigate from the navbar back to the home page.

Create a HistoryRouter component & wrap your navbar or similar & then create react browser router separately for your routes.

import {createBrowserRouter, Link, unstable_HistoryRouter as HistoryRouter} from "react-router-dom";


export const history = createBrowserHistory({ window });

const App = () => {
    return (
       <>
          <HistoryRouter history={history}>
             <Navbar />
           </HistoryRouter>
          <RouterProvider router={router} />
       </>
    );
}

Then create a function that uses the history object to push the new path in the window's location.

import {history} from "../app";


export const logout = (to: string): void => {
    // e.g removeToken();
    history.push(to);
    window.location.reload();
};

I can now use the logout function in my navbar, for example:

<nav>
    <button onClick={_ => logout("/")}>Logout</button>
</nav>
Joe Gasewicz
  • 1,252
  • 15
  • 20