Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Last active October 28, 2016 21:46
Show Gist options
  • Save ryanflorence/78ae1c7fd863812c388c10369e449971 to your computer and use it in GitHub Desktop.
Save ryanflorence/78ae1c7fd863812c388c10369e449971 to your computer and use it in GitHub Desktop.
// this `<Route>` component might make it possible for you to ~generally~
// upgrade to React Router v4 w/o doing hardly anything to your app for the
// happy path stuff.
//
// This definitely won't work as-is, I haven't run this code, just banged it
// out when the thought occured to me that it might be possible.
const { func, object } = React.PropTypes
const Route = (route) => (
<Match pattern={route.path} render={(props) => (
route.children ? (
<MatchWithRouteLifecycle route={route} {...props}>
<MatchGroup>
{React.Children.map(children, (child) => (
<Route {...child.props}/>
))}
</MatchGroup>
</Component>
) : (
<MatchWithRouteLifecycle route={route} {...props}/>
)
)}/>
)
class MatchWithRouteLifecycle extends React.Component {
static propTypes = {
route: {
component: func,
getComponent: func,
onEnter: func,
onChange: func,
onLeave: func,
components: object, // NOPE
getComponents: func, // NOPE
getChildRoutes: func // NOPE
},
location: object,
path: string,
pattern: string
}
constructor(props) {
super(props)
this.state = {
prevProps: null,
needingToRunEnterHook: props.route.onEnter,
needingToGetComponent: props.route.getComponent,
AsyncComponent: null
}
}
componentDidMount() {
if (this.state.needingToRunEnterHook) {
this.runEnterHook()
}
if (this.state.needingToGetComponent) {
this.getComponent()
}
}
componentWillReceiveProps(nextProps) {
if (!locationsAreEqual(nextProps.location, this.props.location)) {
if (this.props.onChange) {
this.runChangeHook(nextHook)
}
}
}
componentWillUnmount() {
if (this.props.route.onLeave) {
this.runLeaveHook()
}
}
getComponent() {
this.props.route.getComponent((err, AsyncComponent) => {
this.setState({ AsyncComponent })
})
}
runLeaveHook() {
const prevState = {}
this.props.route.onLeave(prevState)
}
runEnterHook() {
const { onEnter } = this.props.route
const isAsync = onEnter.length === 3
const nextState = {}
const replace = () => {}
if (isAsync) {
onEnter(nextState, replace, () => {
this.setState({ needingToRunEnterHook: false })
})
} else {
onEnter(nextState, replace)
this.setState({ needingToRunEnterHook: false })
}
}
runChangeHook(nextProps) {
const { onChange } = this.props
const isAsync = onChange.length === 4
const prevState = {}
const nextState = {}
const replace = () => {}
if (!isAsync) {
onChange(prevState, nextState, replace)
} else {
this.block(() => {
onChange(prevState, nextState, replace, this.unblock)
})
}
}
block(cb) {
this.setState({ prevProps: this.props }, cb)
}
unblock() {
this.setState({ prevProps: null })
}
render() {
if (
this.state.needingToRunEnterHook ||
this.state.needingToGetComponent
) {
return null
} else {
const props = this.state.prevProps ?
this.state.prevProps : this.props
const { route, params, location, children } = props
const Component = this.state.AsyncComponent || route.props.component
const v3Props = { ...route, params, location, children }
return <Component {...v3Props}/>
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment