-
Notifications
You must be signed in to change notification settings - Fork 637
State storage in router location vs. Redux store #288
Description
Hi all - thanks for all the great work lately on 4.x.x. Apologies for opening another "existential discussion" ticket but I wanted to ask a few questions about the react-router-redux
state model. I'm working on my first Redux app after several projects in Reflux, so some of this may just be some misunderstanding of how Redux is supposed to work.
Namely I want to question the axiom that "You should not read the location state directly from the Redux store." I know that it's currently unsupported due to react-router
's asynchronicity, but I guess I'm wondering if there's a way to make it work. My use case is something like this:
I have a "page" in my SPA that is an interactive chart with a bunch of settings (filters, time ranges, that kind of thing). I'd like to store them in app state because some of the settings may be used by other components on other pages. So I wire the thing up with an UPDATE_CHART_SETTINGS
Redux action and a chartReducer
that maps the settings into state.chart.settings
and it all works like a charm.
The thing is, I know that in a few weeks, the boss will come tell me that "settings X, Y and Z are the most important, we need to save them in the query string so that people can share links and see the same thing." So, (if I understand the docs correctly,) at that point I will need to add a history.pushState
somewhere, either in the component onChange
callback or (preferably?) in my action, to push the relevant settings to the URL when they change - so far so good. But I will also need to change how this state gets propagated to my components - I have to change my chart container's mapStateToProps
to use props instead of Redux state, and I also have to thread react-router
's location prop down to it, since it's not a top level Route
in this case. And I have to do this for any other components which access this setting.
This seems like a code smell to me - fundamentally, the settings are all still the same kind of app state, and it feels strange to have to handle them differently based on which ones are "saved" in the URL. I think I would be mostly happy if react-router-redux
could provide me with a way to know when it was safe to use the state stored in state.routing.location
(ie. when the component tree matched state) - maybe something like a boolean state.routing.transitioning
that, when true
[edit: oops, meant false
], guaranteed they were in sync.
But to go a bit further - the above solution would still require moving the Single Source of Truth about some of the settings out of state.chart.settings
and into state.routing.location.query
- it would be even better if I could just leave them where they were! Again, "URL-savedness" shouldn't dictate where the setting state is stored, IMO. I think in my perfect world, I would be able to define two reducer-esque functions somewhere that looked something like:
const querySettingKeys = ['filters', 'range'];
const paramSettingKeys = ['id'];
export function mapLocationToState(location, state) {
switch(location.pathname) {
case "/chart":
const settings = Object.assign({}, state.chart.settings,
_.pick(location.query, querySettingKeys),
_.pick(location.params, paramSettingKeys)
);
return _.merge({}, state, {chart: {settings}});
default:
return state;
}
}
export function mapStateToLocation(state, location) {
const query = _.pick(state.chart.settings, querySettingKeys);
const params = _.pick(state.chart.settings, paramSettingKeys);
return _.merge({}, location, {query, params});
}
...and then wire them up to react-router-redux
to keep them in sync. Something along these lines seems to me like it would a good functional approach to passing state between location
and store
- and it would be easyish to unit test by ensuring that mapStateToLocation(mapLocationToState(someLocation)) === someLocation
. It would be great to at least get some feedback to know if this falls into the category of A) hmm, interesting... B) trivially possible already somehow, or C) crazy ramblings of a naive Redux dev who has not yet learned the Way. :)
Thanks very much for your time!