React-router: history.push not working when using BrowserRouter

Created on 18 Oct 2016  路  30Comments  路  Source: ReactTraining/react-router

_startup file_

import {BrowserRouter} from 'react-router';
let root=<BrowserRouter>
                     //Some components
              </BrowserRouter>
 ReactDOM.render(Root, document.getElementById("app"))

_function to navigate or route_

import createBrowserHistory from 'history lib createBrowserHistor';
const history = createBrowserHistory();
let browseTo=function(somepath){
           history.push({pathname: somepath})
})

After calling above Function only URL path changing actual route is not perform.
Like URL localhost:8080 changes to localhost:8080/login not routing to login component.

_Also tried_

import { browserHistory } from './react-router'
let browseTo=function(somepath){
          browserHistory.push('/some/path')
}

but have error browserHistory is undefined

If i use this.context.router.transitionTo('/Some/Path') its working but it is not proper solution

Most helpful comment

@timdorr IMO the problem with the documentation is that none of it is clear to understand or use

  • It feels like almost no examples show what's been imported/exported
  • All examples are all written as though it's a single file in context when in real world cases you're not in a single file
  • Some examples don't make sense, they have classes with just jsx beneath it or objects that aren't clear what they are for or how they are received/obtained
  • Everything is written at a level that seems to assume that you already understand everything that it's telling you - that's not very beginner friendly

I've so far spent the past month in my spare time trying to understand v4 router and it feels ridiculously hard if not impossible to get my head around. The documentation seems to be trying to be minimalist and comes across as though whoever wrote it didn't have much consideration for anyone starting out with this.

All and all, this is just extremely hard for any beginner/intermediate to understand or attempt to implement - quite aside from the fact that it's so different to the previous implementations.

All 30 comments

So it looks like you're trying to mix and match react-router v2 and v4, which are _very_ different.

A couple of points:

  1. Creating a new browserHistory won't work because <BrowserRouter> creates its own history instance, and listens for changes on that. So a different instance will change the url but not update the <BrowserRouter>.
  2. browserHistory is not exposed by react-router in v4, only in v2.

If you want the full history object you can also grab that off context like router.

this.context.history.push('/path')

Using context its working but I am navigating through custom _browseTo_ library _function_.
then need to pass current _context_ as a parameter to function every time.
Is there another approach without _context_

Not at the moment, the history instance is internal to the BrowserRouter component. so the only way is to grab it off context. Some similar discussions in this issue: #4005

In our app, we navigate programmatically via a redux middleware that uses history.

The prime reason for this is that we need to log navigation actions for audit trail purposes.

So we inject history to the store/middleware. Note that the store (<Provider>) is the parent of the router.

Now in V4 <BrowserRouter> derives from <Router> to which you can inject history. This seems to work:

<BrowserRouter history={ history }>

Am I missing something?

@Izhaki You should use <Router history={history}>. The preconfigured routers ignore the history prop.

@timdorr The issue with this is that <Router> doesn't quite lends itself to tests with frameworks like JSDOM.

@timdorr is there any big difference between <Router> and <BrowserRouter>. I'm using the history prop right now, I'd want to be sure that I'm not missing anything by using <Router> over <BrowserRouter>

Just look at the code: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js

No magic. It's just one line to create a history instance and then it wraps <Router>.

I have managed to acquire the history object by passing it as props from container to component, like so:

  • index.js
<BrowserRouter>
    <App />
</BrowserRouter>
  • App.js:
class App extends React.Component {
    render() {
        return (
            <div>
                <NavigationBar />
                {routes.map((route, index) => (
                    <Route
                        key={index}
                        path={route.path}
                        exact={route.exact}
                        component={route.component}
                    />
                ))}
            </div>
        );
    }
}
  • SignupPage (a route):
class SignupPage extends React.Component {
  render() {
    const { userSignupRequest } = this.props;
    return (
      <div className="container">
        <div className="row">
          <div className="col">
            <SignupForm signupRequest={userSignupRequest} history={this.props.history} />
          </div>
        </div>
      </div>
    );
  }
}
  • SignupForm:
handleSubmit(ev) {
        ev.preventDefault();
        if (this.formIsValid()) {
            this.setState({ errors: {} })
            this.props.signupRequest(this.state).then(
                () => {
                    this.setState({ isLoading: false });
                    this.props.history.push('/');
                },
                err => this.setState({ errors: err.response.data, isLoading: false })
            );
        }
    }

....

SignupForm.propTypes = {
    history: PropTypes.object.isRequired,
    signupRequest: PropTypes.func.isRequired
}
  • package.json:
"dependencies": {
...
    "react-router": "^4.1.1",
    "react-router-dom": "^4.1.1",
...
}

It works perfectly!
I hope it helps others as well! :)

@razvantomegea you havent shown how you call SignupPage

Here is the repository of the shown app. Have a look!
https://github.com/razvantomegea/react-fullstack

You can also you withRouter to get an history in your props

@timdorr Not sure why my comment was deleted, was hoping to see the working example from @razvantomegea .

I'm trying to keep old, closed issues from being revived. I don't want to lock things completely, but this issue (amongst many others) needs to die 馃敧

But, but the real problem is, how to use history outside of component. Yes, you can use this.props.history inside a component, now I wanna use history not in a component? but, in a pure javascript function, not inside component. How to do that? withRouter seems doesn't work or broken, component not updating.

Define a history instance yourself and then pass it into a plain <Router>. You can use that history as a singleton or pass it around however you need to.

@timdorr agreed on the issue killing, but after 3 full days trying to understand what was wrong with my code (react newbie here, I admit) this line saved my life:

You should use <Router history={history}>. The preconfigured routers ignore the history prop.

So kill the issues, but please, PLEASE, expand on the docs when something gets tricky (everyone was used to get history in other ways and everyone, and I do mean all the blog posts on the whole internet, is pointing this use case wrong)

Anyways, thanks a lot for your time and effort, it sure was driving me mad

That's a lot @CGastrell, I'm checking if this solves the history issues I've been stuck on.

Edit Sadly this doesn't solve my issue where history.push navigates to the new page, then near instantaneously navigates forward again to the first page.

For example, starting out with no history beyond page load:

before <a> click

Then click a link.. and near instantly land at:

after <a> click

The only workaround I've found is very ugly; adding a set window.location after history.push:

history.push(tab.props.value);
window.location.href = window.location.href;

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route } from 'react-router-dom';
import injectTapEventPlugin from 'react-tap-event-plugin';

import App from './App';
import Foo from './Foo';

import registerServiceWorker from './registerServiceWorker';

injectTapEventPlugin();

ReactDOM.render(
    <BrowserRouter>
        <div>
            <Route exact path="/" component={App} />
            <Route exact path="/foo" component={Foo} />
        </div>
    </BrowserRouter>,
    document.getElementById('root')
);

registerServiceWorker();

@timdorr The problem with killing threads like this is that new coders are running into this problem still, and the documentation either doesn't do a good job describing how to use it, or isn't documented well enough to be easy to find for non-experts. This is a required, dependent API, yet it's very complicated in its use. One potential issue is that the tutorials that came out earlier this year on react are using the older version, but people want to use the newer version (like myself).

I just spent 3 hours on this issue, and finally found someone that told me I can use this.props.history... go figure.

Well put @soulglider009 , you've crystalized the root of the issue perfectly.

If you have an issue with the documentation, just saying "they suck" isn't going to do anything. Specific criticisms help, but even better is a PR to fix those issues. We are always open to improving our documentation. I maintain Redux and it's claim-to-fame is great, community-built documentation.

By all means, go here and take a crack at it. We want it to get better, but need specific guidance on what is not working.

@timdorr IMO the problem with the documentation is that none of it is clear to understand or use

  • It feels like almost no examples show what's been imported/exported
  • All examples are all written as though it's a single file in context when in real world cases you're not in a single file
  • Some examples don't make sense, they have classes with just jsx beneath it or objects that aren't clear what they are for or how they are received/obtained
  • Everything is written at a level that seems to assume that you already understand everything that it's telling you - that's not very beginner friendly

I've so far spent the past month in my spare time trying to understand v4 router and it feels ridiculously hard if not impossible to get my head around. The documentation seems to be trying to be minimalist and comes across as though whoever wrote it didn't have much consideration for anyone starting out with this.

All and all, this is just extremely hard for any beginner/intermediate to understand or attempt to implement - quite aside from the fact that it's so different to the previous implementations.

@South-Paw hate to disagree with you.
I like this doc because in a way, it's very easy to have all in one place and understand it.
Indeed, you need to be familiar with React to understand it and especially with the (props) => (jsx) syntax. But it doesn't look like it's too much to require a correct understanding of react before learning react-router.

Also, please consider that React-v4 is something very new by its design. There is no-to-few similar "Dynamic Router". This is a very cutting edge thing and I'm sure that tutorials like "React for beginners" or "React Router for noobs" are going to pop all over the web in a few months making this more accessible to beginners.

TBH, it took me 2 days to understand the usage and migrate a full production app from react-router v2 to react-router v4. And I don't consider myself as an expert. I've been doing this kind of thing for less than a year.

I really wanted to show some 鉂わ笍 to the contributors and commiters 馃帀

I feel I need to further explain my particular confusion with the docs. Because (it must be said) docs are fine. Really.

I just feel that a bigger heads up regarding history should be included. The guides are really nice, I just missed the difference between react-router-dom and react-router, but the particular .md (which I failed to find) was quite clarifying (after having found this very issue/comment/thread).

Hence, I believe, the issue is that in order to find this _clarifying_ doc I had to:

  • find the repo
  • ignore the beautiful link to docs (which leads to another site, so the rest of the docs on github remain ignored)
  • follow the link to Router (react-router)
  • find nothing too useful on the main readme (regarding history)
  • check the docs folder on the react-router repo
  • check guides folder on the repo (which, you know, newbies like me tend to be attracted to)
  • go back to realize there's an api folder
  • check on the history.md to find not-too-helpful readme
  • go back and (finally) check the Router.md file

Just then (and fighting the urge to read only the code blocks) I found:

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.

As I said, the docs are really fine. Seems to me that being this such a tricky way to cope with the history issue, it should be pointed out more strongly.

So, all that said, I also want to show some love for the contributors and commiters, this library is fantastic and I wouldn't have achieved what I needed without it. I just feel the clarifying paragraph should be pointed out more strongly, or maybe even stated somewhere more _findable_ (higher on the docs tree?)

Hope this helps whoever comes next with some similar issue. Love you guys.

@thedamfr I understand what you mean but the fact is that those resources do not exist (yet) and may not for some time.

Many other react components provide a range of documentation from beginner to expert and in between with solid examples of use including exports/imports (mainly where certain things come from) and multiple use cases (such as nested pages, queries and other use cases) - but I'd argue that react router does not do very will with any real examples of in app use (however, I acknowledge they are there they are not approachable to myself or other's I've asked about and discussed with).

That being said however, I did not intend for the comment to be seen as criticism of the contributors and committers - they have put in an awful amount of work and at the end of the day this is free and open source software 鉂わ笍

It's just that since this would seem to be such a core piece of a react apps circuitry, then we really need those documents for all levels and I'd say; more approachable documentation.

While this may be a very cutting edge way of doing new react components, the fact is the community is not used to them and beginners are certainly not either.

@thedamfr @timdorr, Unfortunately, I'll have to agree with @South-Paw here.

I am fairly new to React (coming from Angular here), and my day job uses far more archaic and complicated stuff, so I solely write React as a hobby. Then I teamed up with a friend to help him write a React app. This was still around v2-v3, so I made sure I learned everything I need from the docs.

Once I thought I got the hang of everything, in came v4, which threw everything I've learned from the past couple months out of the window. I'm a slow learner, and realising that you have to go back to the drawing board and threw everything out of whack.

Not only you have to figure out by yourself what has changed by re-reading the entire docs (and having to look hard to relocate the v3 docs to figure out any differences), the lack of documentation regarding the whole change + community guides/support is my core source of frustration in trying to learn v4, almost to the point where I wanted to just throw in the towel and go back to Angular...

I don't know if I'm not looking hard enough through the official docs, but most of them feel like they're trying to sell the idea of dynamic routing, whilst ignoring the fact that this is a massive rethinking of how routing works and some form of change management is necessary. There's no migration guide.

Don't get me wrong, I believe that what you're doing w/ react-router is great, and I don't mean this to become yet another critique to the devs, as @South-Paw has voiced most of my frustrations already. Once there's enough community support with react-router v4, I'll be sure to come back and finally master React once and for all.

Here's my hack (this is my root-level file, with a little redux mixed in there - though I'm not using react-router-redux):

const store = configureStore()
    const customHistory = createBrowserHistory({
      basename: config.urlBasename || ''
    })

    ReactDOM.render(
      <Provider store={store}>
        <Router history={customHistory}>
          <Route component={({history}) => {
            window.appHistory = history
            return (
              <App />
            )
          }}/>
        </Router>
      </Provider>,
      document.getElementById('root')
    )

I can then use window.appHistory.push() anywhere I want (for example, in my redux store functions/thunks/sagas, etc) I had hoped I could just use window.customHistory.push() but for some reason react-router never seemed to update even though the url changed. But this way I have the EXACT instance react-router uses.

joaoreynolds' solution works, but it breaks my jest tests for sagas. I now get an "Cannot read property 'push' of undefined" error.
Haven't figured out how to fix this.
Code snippets:
saga:

export function* doDeleteResidentSucceeded() {
const message = 'Resident deletion successful!';
yield all([
  put({
    type: types.DELETE__RESIDENT__REQUEST__COMPLETED
  }),

  put({
    type: types.ADD__NOTIFICATION,
    payload: {
      notification: {
        level: 'success',
        message: message,
        residentDeleted: true
      }
    }
  }),
  call(window.appHistory.push, '/residents')
]);

jest test:

describe('doDeleteResidentSucceeded', () => {
  it('returns a success message', () => {
    const generator = saga.doDeleteResidentSucceeded(
      fakeDeleteResidentSuccessAction
    );
    const fakeMessage = 'Resident deletion successful!';

    expect(generator.next(fakeDeleteResidentSuccessAction).value).toEqual(
      all([
        put({
          type: types.DELETE__RESIDENT__REQUEST__COMPLETED,
          payload: { id: fakeResidentId }
        }),
        put({
          type: types.ADD__NOTIFICATION,
          payload: {
            notification: {
              level: 'success',
              message: fakeMessage,
              residentDeleted: true
            }
          }
        }),
        call(window.appHistory.push, '/residents')
      ])
    );
  });
});

@ahmeier I'm not super familiar with sagas, but my guess is you need to setup the appHistory object and mock the push function. I actually just do this in my jest setupTestFrameworkScriptFile.

window.appHistory = {
  push: jest.fn()
}

Hope this helps.

@joaoreynolds Well, I feel silly, but your mock suggestion works very well, thanks.

Was this page helpful?
0 / 5 - 0 ratings