react-router: react-router-redux's push() not rendering new route

First of all, sorry if title is misleading.

I’ve problems with using react-router-redux@next with react-router-dom@4.0.0 I set up all correctly (I’m migrating from V3) and when I dispatch(push(..)) I can see in DevTools action @@router/LOCATION_CHANGE is triggered. Also URL changes. But components are not rendered. However it works without issues with router’s Link

Version

react-router-rom 4.0.0 react-router-redux 5.0.0-alpha.4

Steps to reproduce

I’m attaching file with simple App containing 3 stateless components and defined 3 routes. Also there’re 3 links using Link component and 3 using react-router-redux’ push (links to the same components). I couldn’t use codepen since I haven’t found react-router-redux and react-redux on cdn. Sorry!

File: https://gist.github.com/dominikbulaj/33c37099a0073278eebbba3a0226f2d2

Expected Behavior

Render components when using react-router-redux

Actual Behavior

I see action @@router/LOCATION_CHANGE being triggered. URL changes but nothing changes on page. Looks like it doesn’t push anything to history object.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 38
  • Comments: 19 (1 by maintainers)

Commits related to this issue

Most upvoted comments

<Router history={history}>
    <App>
      <Switch>
        <Route exact path="/" component={indexComponent} />
        <Route path="/login" component={LoginView} />
        <Route path="/partner" render={renderPartner} />
      </Switch>
    </App>
</Router>

In case if someone has a code like this, it’s important to use WithRouter with a parent component, for example, App. So, the parent component will be able to detect changes in location object.

I figured out the best way to do this using react-router 4. BrowserRouter internally creates its own history object that does not work as expected. That’s what was causing the problem for me. I solved it by avoiding BrowserRouter, using Router instead and creating my own history object:

import { Router, Route } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';

const history = createHistory();

const App= () => (
   <Router history={history}>
      <div>
         <Route path='/mypath' component={<MyComponent history={history} />}
      </div>
   </Router>
);

And then inside MyComponent, use this to navigate to a new URL:

this.props.history.push('/myurl');

I tried wrapping all my connected components with withRouter, but it still doesn’t seem to work. My structure is the following:

App.jsx:

<Route component={Home} />

Home.jsx:

class Home extends React.Component {
  ...
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Home))

The URL changes but the route does not render.

@dominikbulaj Do you call createHistory() multiple times? I created History from different files and I had same issue. And I solved it after I import history from myHistory.js file.

// myHistory.js
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
export default history;

import myHistory.js anywhere you called createHistory():

// example: AdminRoutes.js
import { ConnectedRouter } from 'react-router-redux';
import history from './myHistory';
import App from '../containers/AppContainer';

/////////////////////////////////// I did create here, now removed!!
//import createHistory from 'history/createBrowserHistory';
//const history = createHistory(});
///////////////////////////////////

const AdminRoutes = (props) => {
	return (
           <ConnectedRouter history={history}>
                <App muiTheme={muiTheme} {...props} />
	    </ConnectedRouter>
	);
}

@josephMG no I call createHistory() once than pass it to routerMiddleware from react-router-redux

So my example is as follows:

// index.js

import createHistory from 'history/createBrowserHistory'
import { Provider } from 'react-redux'
import configureStore from './configureStore'
// ...

const history = createHistory() // <-- createHistory() call
const store = configureStore(history) // <-- createHistory() response passed to create store

ReactDOM.render(
  <Provider store={store}>
    <App>
      <Router history={history}/>
    </App>
  </Provider>,
  document.getElementById('root')
)

And inside configureStore I have just:

// configureStore.js

import multi from 'redux-multi'
import thunkMiddleware from 'redux-thunk'
import { routerMiddleware } from 'react-router-redux'
// ...

const configureStore = (browserHistory) => {

  let middleware = [
    thunkMiddleware, // lets us dispatch() functions
    routerMiddleware(browserHistory), // <-- here I put createHistory() response/value
    multi
  ]

    return createStore(
    reducers,
    applyMiddleware(
      ...middleware
    ))
}

export default configureStore

The export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Component)) doesn’t work for me too 😦

react-router-rom: 4.0.0 react-router-redux: 5.0.0-alpha.4

If my opinion matters to anyone, I got no luck using withRouter HOC. As @dominikbulaj , I got everything working replacing BrowserRouter with ConnectedRouter from react-router-redux. Simply follow the example in the README. To me, this seems the way to go if you’re using Redux. First because it enables time traveling and keeps state in sync. Second and most important, it forces components to re-render since the state changes. This way, you don’t need to worry about keeping track of the components you need to wrap with withRoute. I also found this video on YouTube that can clarify things a bit.

@MtDalPizzol solution worked for me. Check out the video.

Basically instead of using: import {BrowserRouter as Router} from 'react-router-dom';

Use: import {ConnectedRouter as Router} from 'react-router-redux';

<Provider store={props.store}>
    <Router history={history}>
    </Router>
</Provider>

@danyim in my case in index.js (entry point) I’ve:

ReactDOM.render(
  <Provider store={store}>
    <App>
      <Router history={history}/>
    </App>
  </Provider>,
  document.getElementById('root')
)

Router is component with ConnectedRouter root element (this comes from react-router-redux

export default ({history}) => <ConnectedRouter history={history}>
  <Switch>
  <Route exact path='/' component={Home}/>
  <Route path="/login" component={Login}/>
  <Route path="/logout" component={Logout}/>
{* ... *}
</Switch>
</ConnectedRouter>

NOTE not every comonent needs withRouter (I’d say I use it in just ~30%)

@naderchehab’s solution worked for me. Wohoo! What a day!

@dominikbulaj Thanks. We were able to figure it out. The boilerplate we were using nested a BrowserRouter inside of a ConnectedRouter that needed to be passed into the withRouter higher order component.