-
-
Save tobyS/9670c1ba1ef3fb1f371e2de21209e975 to your computer and use it in GitHub Desktop.
class PriceRange extends PureComponent { | |
constructor (props) { | |
super(props) | |
this.state = { | |
range: null, | |
} | |
} | |
// Overwrites the state if page just loaded or other | |
// component changed the URL | |
static getDerivedStateFromProps = (props, state) => { | |
if (!state.range || !props.valueFromComponent) { | |
return { | |
range: [ | |
props.value.min, | |
props.value.max, | |
], | |
} | |
} | |
return null | |
} | |
render () { | |
// Actual range slider UI element (https://github.com/react-component/slider) | |
return (<Range | |
value={this.state.range} | |
onChange={(range) => { | |
this.updateRange(range) | |
}} | |
/>) | |
} | |
updateRange = (range) => { | |
this.setState({ | |
range: _.cloneDeep(range), | |
}, this.updateUrl) | |
} | |
updateUrl = _.debounce(() => { | |
app.getRouter().push( | |
app.getRouter().route, | |
{ | |
min: this.state.range[0], | |
max: this.state.range[1], | |
}, | |
// Indicate in URL state that this | |
{ valueFromComponent: true } | |
) | |
}, 100) | |
} |
Looks like a missing application logic layer between UI components and external side effects like browser url changes. This can be solved with Redux by separating the "component <-> browser url" sync into "component <-> store" and "store <-> browser url". The former can be implemented with Reducers and the connect()
function of react-redux
. The latter handles the side effect and can be implemented with a custom Redux middleware. The middleware should react to Redux actions indicating a slider change by changing the browser url. It should also monitor url changes and emit certain Redux actions to which the Reducer mentioned above can react to which consequently will trigger a rerender of connected components. The middleware might also take care of the whole debouncing stuff. If you are looking for a way how to handle url sync the Redux way, you might wanna take a look at connected-react-router
which is basically just a Redux binding for react-router
.
This is the current solution we found for synchronizing our slider component with the URL. Its an ugly solution, IMO, but it works. You know a better solution? Please leave a comment!
The problems we were facing are:
NOTE: This is a stripped down example from production code. This is not meant to run but only to illustrate the code without too much noise!
The goal of this component is the following:
We currently use the history state to indicate that the component itself is the source of the range parameters. If this is the case, we already saw the range in the component and therefore do not need to reset the component state to the incoming value. If this is not the case, something else (new page load, other component, etc.) changed the URL parameters and we should reflect it.