Access react router dom history object outside React component

Viewed 667

I am looking for a good way to use the history object of react-router-dom anywhere in the app. No matter it's a react component or not.

The solution I thought might be good was using the window object.

in App.jsx

import React from "react";
import { useHistory } from "react-router-dom";
import { getUser } from "./axios";

const App = () => {
  let history = useHistory();

  if (!window.reactHistory) {
    window.reactHistory = history;
  // attach history object to window object. 
  }

  React.useEffect(() => {
    getUser();
  }, []);
  return (
    <div className="App">
    </div>
  );
};

export default App;

in Index.js nothing too fancy,

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BrowserRouter as Router } from "react-router-dom";
const rootElement = document.getElementById("root");
ReactDOM.render(
  <StrictMode>
    <Router>
      <App />
    </Router>
  </StrictMode>,
  rootElement
);

In api.js

import axios from "axios";

export const getUser = async () => {
  try {
    const res = await axios.get("");

    if (res.status === 401) {
      if (window.reactHistory) {
        console.log("reactHistory :", window.reactHistory);
        window.reactHistory.push("/login");
        // access react-router-dom history via window object
      }
    }
  } catch (err) {}
};

Used react-router-dom version is 5.3.0. You can find the code above in my codesandbox. https://codesandbox.io/s/goofy-yonath-o13kcq?file=/src/App.js

I tested this approach and it's worked well so far. But no one seems to use the history object this way and I wondered whether it's a really good way.

I'd appreciate it if you give your opinion. Thanks!

1 Answers

Normally you'd create a history object using createBrowserHistory (or the matching variant for the router you use) and pass this to the low level router. Import the created history object where needed.

See Router

The common low-level interface for all router components. Typically apps will use one of the high-level routers instead:

  • <BrowserRouter>
  • <HashRouter>
  • <MemoryRouter>
  • <NativeRouter>
  • <StaticRouter>

The most common use-case for using the low-level is to synchronize a custom history with a state management lib like Redux or Mobx. Note that this is not required to use state management libs alongside React Router, it’s only for deep integration.

import React from "react";
import ReactDOM from "react-dom";
import { Router } from "react-router";
import { createBrowserHistory } from "history";

const history = createBrowserHistory();

ReactDOM.render(
  <Router history={history}>
    <App />
  </Router>,
  node
);

Since you are using react-router-dom@5 then ensure you have installed history@4 version. The latest history@5 version works with the latest react-router-dom@6 version.

  • Create and export a history object.

      import { createBrowserHistory } from "history";
    
      export default createBrowserHistory();
    
  • import your history object and instantiate the router.

      import React from "react";
      import ReactDOM from "react-dom";
      import { Router } from "react-router";
      import history from "../path/to/history";
    
      ReactDOM.render(
        <Router history={history}>
          <App />
        </Router>,
        node
      );
    
  • import your history object in api.js.

      import axios from "axios";
      import history from "../path/to/history";
    
      export const getUser = async () => {
        try {
          const res = await axios.get("....");
    
          if (res.status === 401) {
            history.push("/login");
          }
        } catch (err) {
          ....
        }
      };
    

For more details creating custom history objects see basic usage.

Related