125

I having some little issue migrating from React-Router v3 to v4. in v3 I was able to do this anywhere:

import { browserHistory } from 'react-router';
browserHistory.push('/some/path');

How do I achieve this in v4.

I know that I could use, the hoc withRouter, react context, or event router props when you are in a Component. but it is not the case for me.

I am looking for the equivalence of NavigatingOutsideOfComponents in v4

Ondrej
  • 502
  • 1
  • 3
  • 12
storm_buster
  • 7,362
  • 18
  • 53
  • 75
  • 2
    Thanks @Chris, but like I said, I am not in Component. – storm_buster Mar 08 '17 at 14:27
  • @Chris in a utility class, please check https://github.com/ReactTraining/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md, it could have been a redux middlewarere or anything else – storm_buster Mar 08 '17 at 17:40
  • simplest way https://stackoverflow.com/a/53916596/3966458 – Saahithyan Vigneswaran Dec 24 '18 at 18:16
  • I ended up using the BrowserRouter as root component. That way I could use the App component withRouter. Not exactly what you asked for but I have the same requirements and this is sufficient for me. – The Fool Sep 04 '19 at 09:42

7 Answers7

205

You just need to have a module that exports a history object. Then you would import that object throughout your project.

// history.js
import { createBrowserHistory } from 'history'

export default createBrowserHistory({
  /* pass a configuration object here if needed */
})

Then, instead of using one of the built-in routers, you would use the <Router> component.

// index.js
import { Router } from 'react-router-dom'
import history from './history'
import App from './App'

ReactDOM.render((
  <Router history={history}>
    <App />
  </Router>
), holder)
// some-other-file.js
import history from './history'
history.push('/go-here')
Paul S
  • 35,097
  • 6
  • 41
  • 38
  • 1
    Thanks a lot! At the moment, using react-router4, I had to use this import in history.js instead: import createBrowserHistory from 'history/createBrowserHistory'; – peter.bartos May 10 '17 at 08:22
  • 3
    It's worked for me when I put history={history} in Route and pass like a props – Nath Paiva May 31 '17 at 22:30
  • 1
    we can use withHistory and get history in props rather than creating separate file. this solution didn't work for me with redux. – Zeel Shah Jun 14 '17 at 14:24
  • Well done, but it's kind of confusing to name the file history, and having the both import 'history' and './history' meaning 2 really different things. – pdem Oct 12 '17 at 10:14
  • 4
    Import `createHashHistory` if you were using `HashRouter`. – David Harkness Dec 01 '17 at 04:53
  • 1
    Thanks a ton for this. It took me awhile to realize that the reason this works is that something like BrowserRouter creates its own instance of the history object and will only listen to changes on its own instance. So, by using the same global singleton, between plain old javascript and the components, a programatic update to one will be reflected in the components. Thanks – justinbc820 Feb 03 '18 at 05:42
  • Paul S I am trying what you have suggested and it works in the sense that component page changes but window.location is not changing with the new URL. Any clue why it might be happening? – cauchy Feb 06 '18 at 11:56
  • @cauchy The two potential issues that immediately come to mind are 1) you are creating a memory history instead of a browser history or 2) your app is running in an – Paul S Feb 06 '18 at 16:29
  • @PaulS I debugged and figured it is happening because of first one but I did that because of I am doing server side rendering so first time when browser it is not available it looks for history. To avoid that I modified `import { createBrowserHistory, createMemoryHistory } from "history" let customHistory if(process.env.BROWSER) { customHistory = createBrowserHistory() } else { customHistory = createMemoryHistory() } export default customHistory ` and it is going to else part. Can you give me some pointer how this situation should be handled? – cauchy Feb 06 '18 at 17:02
  • @cauchy You could probably check if `window` is defined for the module to determine whether to create a browser or memory history. – Paul S Feb 07 '18 at 05:51
  • May I ask what does ` import { createBrowserHistory } from 'history'` does? I see you are importing from the history file and you are on history file. – Unity Hour Jul 02 '18 at 13:07
  • @UnityHour the absolute import of "history" is importing from the history package (http://npmjs.com/package/history), while the local module "history.js" is for your local history instance. – Paul S Jul 02 '18 at 18:01
  • The only correct answer is below: import { withRouter } from 'react-router-dom'; – user2434435 May 08 '19 at 21:14
  • update to avoid deprecations - `import { createBrowserHistory } from 'history/createBrowserHistory';` – Eric Hasselbring Aug 13 '19 at 19:25
  • The history.js module may be a little more explicit by writing it this way: ```import { createBrowserHistory } from 'history';``` ```const history = createBrowserHistory()``` ```export default history``` – RancheroBeans Oct 18 '19 at 00:06
  • That's exactly what i'm doing but still not working, the URL changes but the components don't update, i always get the 404 component i don't know why. – Hassan Azzam Jul 07 '20 at 19:45
  • 3
    @HassanAzzam You are probably using react-router V5, and this answer works for V4, not V5. I have right now the same challenge, and am looking everywhere, but unable to make it work. history.push outside of component works, but clicks on the Link don’t anymore. – Waterlink Jul 18 '20 at 16:43
  • how to to do this with browserrouter and useHistory() hook, outside component? – vikrant Aug 10 '20 at 12:26
  • @HassanAzzam use history@4.x.x for react router V4 and V5 to resolve your issue. – Deepak K J Aug 15 '20 at 00:02
  • @HassanAzzam, are you wrapping your routes in ``? The `location` object is structured differently when using `createBrowserHistory` (if you do console.log(location), you'll see it return {action: "PUSH", location: {...}}}) - maybe that's throwing off the Swtich? – user51462 Dec 31 '20 at 22:51
  • Remember to use `Router` not `BrowserRouter`. As other people have commented. – Xalsar Mar 09 '21 at 07:51
80

This works! https://reacttraining.com/react-router/web/api/withRouter

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

class MyComponent extends React.Component {
  render () {
    this.props.history;
  }
}

withRouter(MyComponent);
Brandon Culley
  • 5,219
  • 1
  • 28
  • 28
Justin Yueh
  • 841
  • 6
  • 6
  • 31
    as long as you are in a component. I am not in a component – storm_buster Jun 27 '17 at 18:02
  • 2
    Thank you, this is the easiest and most straightforward way to link to another route from a component. I would just add that you need to do `this.props.history.push('/some_route');` in order for it to work. – dannytenaglias Sep 06 '17 at 19:12
  • 1
    @storm_buster `const Component = props => {... // stuff}` and `export default withRouter(Component)` works for me on a stateless component – Karl Taylor Jan 22 '18 at 17:53
15

Basing on this answer if you need history object only in order to navigate to other component:

import { useHistory } from "react-router-dom";

function HomeButton() {
  const history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}
Konrad Grzyb
  • 1,561
  • 16
  • 12
13

Similiary to accepted answer what you could do is use react and react-router itself to provide you history object which you can scope in a file and then export.

history.js

import React from 'react';
import { withRouter } from 'react-router';

// variable which will point to react-router history
let globalHistory = null;

// component which we will mount on top of the app
class Spy extends React.Component {
  constructor(props) {
    super(props)
    globalHistory = props.history; 
  }

  componentDidUpdate() {
    globalHistory = this.props.history;
  }

  render(){
    return null;
  }
}

export const GlobalHistory = withRouter(Spy);

// export react-router history
export default function getHistory() {    
  return globalHistory;
}

You later then import Component and mount to initialize history variable:

import { BrowserRouter } from 'react-router-dom';
import { GlobalHistory } from './history';

function render() {
  ReactDOM.render(
    <BrowserRouter>
        <div>
            <GlobalHistory />
            //.....
        </div>
    </BrowserRouter>
    document.getElementById('app'),
  );
}

And then you can just import in your app when it has been mounted:

import getHistory from './history'; 

export const goToPage = () => (dispatch) => {
  dispatch({ type: GO_TO_SUCCESS_PAGE });
  getHistory().push('/success'); // at this point component probably has been mounted and we can safely get `history`
};

I even made and npm package that does just that.

Tomasz Mularczyk
  • 34,501
  • 19
  • 112
  • 166
6

If you are using redux and redux-thunk the best solution will be using react-router-redux

// then, in redux actions for example
import { push } from 'react-router-redux'

dispatch(push('/some/path'))

It's important to see the docs to do some configurations.

Arnold Gandarillas
  • 3,896
  • 1
  • 30
  • 36
-1

In App.js

 import {useHistory } from "react-router-dom";

 const TheContext = React.createContext(null);

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

   <TheContext.Provider value={{ history, user }}>

    <Switch>
        <Route exact path="/" render={(props) => <Home {...props} />} />
        <Route
          exact
          path="/sign-up"
          render={(props) => <SignUp {...props} setUser={setUser} />}
        /> ...

Then in a child component :

const Welcome = () => {
    
    const {user, history} = React.useContext(TheContext); 
    ....
Squirrl
  • 4,909
  • 9
  • 47
  • 85
-2

In the specific case of react-router, using context is a valid case scenario, e.g.

class MyComponent extends React.Component {
  props: PropsType;

  static contextTypes = {
    router: PropTypes.object
  };

  render () {
    this.context.router;
  }
}

You can access an instance of the history via the router context, e.g. this.context.router.history.

Gajus
  • 69,002
  • 70
  • 275
  • 438
  • The question specifically is asking about when outside of a component so this.context is not available as an option: > "I know that I could use, the hoc withRouter, react context, or event router props when you are in a Component. but it is not the case for me." – SunshinyDoyle Nov 16 '17 at 18:02