2

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!

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Shawn
  • 367
  • 4
  • 13

2 Answers2

1

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 react-router-dom@6 version until v6.4 when the Data APIs were introduced.

  • 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.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
0

In React Router v6 this has now been addressed. By creating a router via your preferred strategy (createBrowserRouter is recommended), you'd pass that router to a <RouterProvider> component - thus your history is created and maintained in RR's internals.

import { createBrowserRouter, RouterProvider } from "react-router-dom";

export const router = createBrowserRouter([
  { path: "*", element: <Index /> }
])

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
)

You can use that router later in a similar way as you would use the old history object, like so:

import router from `App`;

// Code outside of the React tree
function (route) {
  router.navigate(route);
}
miphe
  • 1,763
  • 1
  • 20
  • 33