Using ‘history’ to navigate your React app from outside a component
If you have done any programming in React, you are probably familiar with basic routing using <Switch>
, <Route>
, and <Link>
. To do this, we import BrowserRouter
, HashRouter
, MemoryRouter
, or NativeRouter
from react-router-dom
and then wrap our <App />
component in our chosen router within index.js
. This would look something like this:
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter} from "react-router";
import App from './App';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
Now we have the freedom to use the aforementioned tags anywhere within our application. However, what you may not know is that whenever we use any of the above listed routers, React creates a history
object for us to use as well and passes that object within the default props. For example, assume no props have been passed to the Login component. Login can still accept the default props and utilize the history object in this way:
import React from 'react';export default function Login(props) {
props.history.push('/home')
}
We would obviously want some additional logic in there, but the point is that we can access the history object through props
, even though no props were passed to Login
, and use history.push
to push a new entry onto the history stack. Based on the above example, this will route the user to the component associated with the /home
url. Well that was easy!
But what happens if we actually pass props to
Login
, thus overwriting the default props containing the history object? Or what about trying to access the history object from outside of a component?
Spoiler…the above approach won’t work! In this case, we need to create our own history object, in its own module, and import it where it is needed in our application.
Note: If you are attempting to use history inside of a component and are using React version 16.8 or later, there is an easier way to accomplish this. See my article Navigating your React app with the useHistory hook instead.
First, we need to create the history module. Create a new JavaScript file called history.js
. Then add the following code to the file:
import {createBrowserHistory} from 'history';
export default createBrowserHistory();
Next, we need to update index.js
to use the generic <Router>
instead of one of the fancy routers that we listed at the beginning of this article. We will also need to import history.js
like so:
import React from 'react';
import ReactDOM from 'react-dom';
import {Router} from "react-router";
import App from './App';
import history from "./history";
ReactDOM.render(
<Router history={history}>
<App />
</Router>,
document.getElementById('root')
);
Now we can utilize history whether or not we are inside a component, and regardless of props, by just importing history wherever it is needed! For example:
import React from 'react';
import history from "./history";export default function Login() {
history.push('/home')
}
There are a number of other properties that are usually included within the history object. In this example we used history.push
, but you could also use:
length
- (number) The number of entries in the history stackaction
- (string) The current action (PUSH
,REPLACE
, orPOP
)push(path, [state])
- (function) Pushes a new entry onto the history stackreplace(path, [state])
- (function) Replaces the current entry on the history stackgo(n)
- (function) Moves the pointer in the history stack byn
entriesgoBack()
- (function) Equivalent togo(-1)
goForward()
- (function) Equivalent togo(1)
block(prompt)
- (function) Prevents navigation
This list is not exhaustive, so I encourage you to dig a little deeper into history
on your own!
Has using history
gotten you out of a bind in the past? Do you think it just might solve a problem you are facing now? I’d love to hear about it!