-
-
Save gaelollivier/050e5cce31f9fabb1030f4ba47663db5 to your computer and use it in GitHub Desktop.
React Router v4 ControlledRouter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import React, { Component } from 'react' | |
import BrowserHistory from 'react-history/BrowserHistory' | |
import { Push } from 'react-history' | |
import { StaticRouter } from 'react-router' | |
/** | |
* ControlledRouter, greatly inspired by https://gist.github.com/donnanicolas/3d76397a92551f449637590bf0413133 | |
* Usage: | |
* <ControlledRouter location={location} setLocation={setLocation}> | |
* <App /> | |
* </ControlledRouter> | |
*/ | |
type PropTypes = { | |
location: Object, | |
setLocation: Function, | |
children?: React.Element<*>, | |
} | |
class ControlledRouter extends Component { | |
props: PropTypes | |
prevPathname: string | |
constructor(props: PropTypes) { | |
super(props) | |
this.prevPathname = '' | |
} | |
render() { | |
const { location, setLocation, children } = this.props | |
return ( | |
<BrowserHistory | |
key={ location.pathname } | |
> | |
{({ history, action, location: historyLocation }) => { | |
const historyPathname = historyLocation.pathname | |
const controlledPathname = location.pathname | |
const pathChanged = historyPathname !== controlledPathname | |
const shouldUpdateState = pathChanged && historyPathname !== this.prevPathname | |
const shouldUpdateHistory = pathChanged && !shouldUpdateState | |
// Keep track of previous pathname | |
this.prevPathname = historyLocation.pathname | |
if (shouldUpdateState) { | |
setTimeout(() => { | |
setLocation(historyLocation) | |
}, 0) | |
} | |
return ( | |
<StaticRouter | |
action={action} | |
location={historyLocation} | |
onPush={history.push} | |
onReplace={history.replace} | |
blockTransitions={history.block} | |
> | |
{ shouldUpdateHistory ? <Push path={location.pathname} /> : children } | |
</StaticRouter> | |
); | |
}} | |
</BrowserHistory> | |
) | |
} | |
} | |
ControlledRouter.displayName = 'ControlledRouter' | |
export default ControlledRouter |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is so close but cause issues. Specifically the
setTimeout
with setLocation inside. I get why you did it: React complains loudly when you update state during a render but this method has insidious effects. For example, a child component will sometimes unmount and then render…Quick fix here: https://gist.github.com/jbraithwaite/4c4ecfbc8a09d63e83d86c6810e0ec77