First of all: thanks sirs for you great work!
I would like the be able to register hooks which will be run before or after location changes.
Add beforeRouteChange and afterRouteChange additional properties on BrowserRouter component which will accept function (or array of functions) run before or after dispatching location changes.
<Match /> instance which will load asynchronously the component <X /><X /> is connected to a redux store<X /> (because the user may never come to the route matched to <X />)In the dumb example below: we will add a lag time of 0.5 seconds before every location change.
class Root extends React.Component {
constructor () {
super()
this.beforeRouteChange = this.beforeRouteChange.bind(this)
}
beforeRouteChange ({location, action}) {
return new Promise((resolve) =>
setTimeout(resolve, this.props.lagTime || 500))
}
render () {
return (
<BrowserRouter beforeRouteChange={this.beforeRouteChange}>
<Link to="/foo">foo</Link>
{' '}
<Link to="/bar">bar</Link>
</BrowserRouter>
)
}
}
class Root extends React.Component {
constructor () {
super()
this.afterRouteChange = this.afterRouteChange.bind(this)
this.beforeRouteChange = this.beforeRouteChange.bind(this)
this.state = {routeLoading: false}
}
beforeRouteChange ({location, action}) {
console.log('navigate to:', location)
this.setState({routeLoading: true})
}
afterRouteChange ({location, action}) {
console.log('route loaded:', location)
this.setState({routeLoading: false})
}
renderLoadingIndictor () {
if (!this.state.routeLoading) {
return null
}
return (
<div>Loading...</div>
)
}
render () {
return (
<div>
{this.renderLoadingIndictor()}
<BrowserRouter
beforeRouteChange={this.beforeRouteChange}
afterRouteChange={this.afterRouteChange}
>
<Link to="/foo">foo</Link>
{' '}
<Link to="/bar">bar</Link>
</BrowserRouter>
</div>
)
}
}
v4 - 553b56a)Note: I wrote the code below for this post only. I haven't test it because I didn't succeed to build react-router from sources (strange "module not found" errors were thrown by babel).
import React, { PropTypes } from 'react'
import StaticRouter from './StaticRouter'
class Router extends React.Component {
constructor(props) {
super(props)
this.state = {
location: props.history.location,
action: props.history.action
}
}
// v +++
componentDidMount() {
const { history, afterRouteChange, beforeRouteChange } = this.props
let lastRouteKey = history.location.key
this.unlisten = history.listen(() => {
const routeKey = lastRouteKey = history.location.key
const nextState = {
location: history.location,
action: history.action
}
;[]
.concat(beforeRouteChange, this.setState.bind(this), afterRouteChange)
.map((f) => () => f && routeKey === lastRouteKey && f(nextState))
.reduce((v, f) => v.constructor === Function ? v() : v.then ? v.then(v => f(v)) : f(v))
})
}
// ^ +++
componentWillUnmount() {
this.unlisten()
}
render() {
const { location, action } = this.state
const { history, ...rest } = this.props
return (
<StaticRouter
action={action}
location={location}
onPush={history.push}
onReplace={history.replace}
blockTransitions={history.block}
{...rest}
/>
)
}
}
if (__DEV__) {
Router.propTypes = {
afterRouteChange: PropTypes.oneOfType([ // < +++
PropTypes.func, // < +++
PropTypes.arrayOf(PropTypes.func) // < +++
]), // < +++
beforeRouteChange: PropTypes.oneOfType([ // < +++
PropTypes.func, // < +++
PropTypes.arrayOf(PropTypes.func) // < +++
]), // < +++
history: PropTypes.object.isRequired
}
}
export default Router
This would be fantastic - flexible, and solves issues like this one from v3:
https://github.com/ReactTraining/react-router/issues/3338
For our purposes, we'd like to introduce an artificial lag / loading (similar to github route changes) because otherwise things literally change on screen TOO fast, sometimes users don't notice :).
Could something like this instead be implemented by composing something such as <LoadableMatch>? It could wrap children in a component that orchestrates the loading state, returning <Match> with a loading indicator, or the component which has access to a loaded redux state.
It's more work, but reduces API surface.
Should be pretty easy to roll your own hooks with v4 through composition, so we shouldn't actually need to expose any more API here. Just an example.
Been eagerly following the recent v4 refactors and progress. I know speaking to timelines is tough, but any idea on the order of magnitude to next release? Days, weeks, months?
I described what I think is a workable solution to this problem in https://github.com/ReactTraining/react-router/issues/4300#issuecomment-274165691. Hopefully that's enough to get the gears turning to figure out how we can solve this problem using composition instead of adding more API.
As for the next release, I'd say we'll have it within a week.
Most helpful comment
Should be pretty easy to roll your own hooks with v4 through composition, so we shouldn't actually need to expose any more API here. Just an example.