mobx-react-router: Using Browser buttons, gets rendered page out of sync with path
My routing seems to work fine. But when I use the browser ‘back’ button, the URL changes correctly, but the page then doesn’t match the URL. The first time I hit the back button, nothing happens. Then second time I click it goes to where it should have gone the first time.
This is my entry point: index.tsx:
{UserStore} from './data/stores/UserStore';
declare let module: any;
import * as React from "react";
import * as ReactDOM from "react-dom";
import {AppContainer} from 'react-hot-loader';
import { Router } from 'react-router';
// import createHistory from 'history/createBrowserHistory';
import createHistory from 'history/createHashHistory';
import { Provider } from 'mobx-react';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
import { App } from "./components/app/App";
const history = createHistory();
const routingStore = new RouterStore();
const users = new UserStore();
const synchronizedHistory = syncHistoryWithStore(history, routingStore);
const stores = {
routing: routingStore,
users: users
};
const renderApp = (Component: any) =>
{
ReactDOM.render(
<AppContainer>
<Provider {...stores}>
<Router history={synchronizedHistory}>
<Component />
</Router>
</Provider>
</AppContainer>,
document.getElementById("app_content")
);
};
renderApp(App);
// Hot Module Replacement API
if (module.hot) {
module.hot.accept('./components/app/App', () => {
renderApp(App)
});
}
My app file:
import 'bootstrap';
import * as React from 'react';
import {TopNav} from '../topnav/TopNav';
import {History} from 'history';
import {Routes} from './Routes';
import {inject, observer} from 'mobx-react';
import {UserStore} from '../../data/stores/UserStore';
import {Level, Logger} from '../../util/Logger';
interface IAppProps { routing: History, users: UserStore }
@inject('routing')
@observer
export class App extends React.Component<IAppProps, undefined>
{
//noinspection JSMethodCanBeStatic,JSUnusedLocalSymbols
private log(msg: string, level?: Level): void
{
Logger.get().log(msg, 'App', level || Level.TRACE);
}
render(): JSX.Element
{
const { location } = this.props.routing;
return (
<div>
<TopNav/>
<div className='container body-content'>
<Routes/>
<div className='small'>path: {location.pathname}</div>
</div>
</div>);
}
}
My routes file:
import * as React from 'react';
import {Route, Switch} from 'react-router-dom';
import {HomePage, PageOne, PageThree, PageTwo} from '../home/HomePage';
import {PendingDeliveryPage} from '../delivery/PendingDeliveryPage';
import {DeliveryExecutePage} from '../delivery/DeliveryExecutePage';
export class Routes extends React.Component<undefined, undefined>
{
public render(): JSX.Element
{
return (
<Switch>
<Route exact path='/' component={HomePage} />
<Route path='/delivery/:id/execute' component={DeliveryExecutePage} />
<Route path='/delivery' component={PendingDeliveryPage} />
<Route path='/one' component={PageOne} />
<Route path='/two' component={PageTwo} />
<Route path='/three' component={PageThree} />
</Switch>
);
}
}
And lastely my test pages:
import * as React from "react";
import {Link, RouteComponentProps} from 'react-router-dom';
export class HomePage extends React.Component<RouteComponentProps<any>, undefined>
{
public render(): JSX.Element
{
return (
<div>
<h1>Home</h1>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/one">One</Link></li>
<li><Link to="/two">Two</Link></li>
<li><Link to="/three">Three</Link></li>
</ul>
</div>);
}
}
export class PageOne extends React.Component<RouteComponentProps<any>, undefined>
{
public render(): JSX.Element
{
return (
<div>
<h1>Page One</h1>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/one">One</Link></li>
<li><Link to="/two">Two</Link></li>
<li><Link to="/three">Three</Link></li>
</ul>
</div>);
}
}
export class PageTwo extends React.Component<RouteComponentProps<any>, undefined>
{
public render(): JSX.Element
{
return (
<div>
<h1>Page Two</h1>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/one">One</Link></li>
<li><Link to="/two">Two</Link></li>
<li><Link to="/three">Three</Link></li>
</ul>
</div>);
}
}
export class PageThree extends React.Component<RouteComponentProps<any>, undefined>
{
public render(): JSX.Element
{
return (
<div>
<h1>Page Three</h1>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/one">One</Link></li>
<li><Link to="/two">Two</Link></li>
<li><Link to="/three">Three</Link></li>
</ul>
</div>);
}
}
You can click
- Start at ‘Home’
- Click ‘One’
- Click ‘Two’
- Click ‘Three’, and everthing should be fine.
- Click Browser Back, path changes to /two, but page does not render anything new.
- Click Browser Back and path changes to /one, and now page /two renders.
- Click Browser Back and page changes to /, but page /one renders.
- etc.
Am i doinng something wrong here?
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Comments: 20
Ok after more investigating it looks like a react-router problem, but not with the switch - with the
Routecomponent update. The Route component uses context to get the current location, and I think theshouldComponentUpdateof the mobx-reactobservercomponent (which wraps App in your example) is returning false. This in turn is messing up the context update.Context is known to be a fairly broken feature, especially when pairing with MobX/Redux, which overwrite
shouldComponentUpdatein places.This needs bringing up with the
react-routerguys.In terms of a workaround - wrapping
App(the component which uses mobxobserver) inwithRouterfrom react-router works for me, as this will overwrite theshouldComponentUpdatefrom observer.Sory about bring this discussion again, turns out that i’ve discovered a problem that i had im my configuration and this may help someone!
Once you create the History with synced Store, you don’t need the React Router
<BrowserRouter>componentYou just initialize with
<Router history={History}>and you’re ready to register and access your routes. My problem was caused by a duplicated initialization of the BrowserRouter in the startup React component:I had
<Router={history}>and also<BrowserRouter>, together only the last one was working and it was breaking the plugin functionality. After removingBrowserRouterwith only<Router history={History}>solved my issue!Was a silly mistake because once you learn how to configure V4 routing and you plug this package into your app everything works fine, but turns out that two Routing are trying to control the app state and just the last one work breaking the package functionality.
Well, at the end just removing
BrowserRouterand let the plugin do his job withsyncHistoryWithStoresolved my issues! Thanks.You probably need to wrap any components which render
<Route />components in thewithRouterhigher order component. That’s the easiest fix. Again I don’t see what can be done from this packages point of view is the issue comes from react-router using the unstablecontextAPI.Sorry guys, but i’m currently having the same issue and can’t find a way to solve it. I need to wrap my entire app with
withRouter?I din’t understood this how to solve this problem.
Thank you! That solved it for me. I was wrapping my Routes class. Wrapping the app class fixed it.
Wrapping App in
withRouterworked for me. You should probably put some console.logs in your components, so you can see what is/isn’t rendering.