redux-query-sync
redux-query-sync copied to clipboard
initialTruth: 'merge'
We now either have to choose whether the URL params initially overwrite the state, or the state overwrites the URL, but sometimes you may want to incorporate information from both.
An example scenario: my application stores user preferences in the local storage, let's say the last viewed x
and y
coordinates on the map. These should also be exposed in the URL, so the user's view can be linked to, but when I visit the page without specifying an x
or y
, they should default to their stored values. Setting the defaultValue
s to the stored values is inappropriate, since it would hide them from the URL.
A solution would be to let the given URL parameters overwrite the values in the state, but for parameters that it does not specify, we let the state write its values to the URL. This only needs to happen on initialisation, after which they are kept in sync. An option initialTruth: 'merge'
might be good for this, perhaps even as a default setting. (the name might have to change, as perhaps we want to also support merging in the other direction, i.e. letting non-default state values override URL params)
I was going to post an issue, but it seems this covers it. On getQueryValues
when the param is not found (valueString === null
) instead of reading the defaultValue
from the params, we could read it from the store using the selector:
Current code:
const value = (valueString === null)
? defaultValue
: stringToValue(valueString)
Proposed change/addition (for merge):
const state = store.getState()
// forEach
const { defaultValue, selector, stringToValue = s => s } = params[param]
// Actual Change
const value = (valueString === null)
? selector(state)
: stringToValue(valueString)
This paired with initialTruth: 'store'
seem to be working as described.
My use case, for the sake of adding examples: It's a Dashboard where changes on the "global" filter are persisted across pages, and are sync to the URL for sharing insights between users. The state already has a default, but selector(state)
always return a value. defaultValue
is simply used to clean the default values from the URL.
EDIT: To support both directions how about mergeStore
and mergeLocation
? I'm ready to help, let's do this. Sorry for the constant edits, I'm actively working on this, how about this for initialValue:
location | store | mergeLocation | mergeStore | |
---|---|---|---|---|
location | {a: 1} |
{a: 1} |
{a: 1, b: 2} |
{a: 1} |
store | {a: 2} |
{a: 2} |
{b: 3} |
{a: 2, b: 3} |
result | {a: 1} |
{a: 2} |
{a: 1, b: 3} |
{a: 1, b: 3} |
Thanks for helping out with this one! I need to find time to think about (and tinker with) this again. One thing I wonder about now is whether your solution puts the merged values back into the location, which is desired to keep things consistent (parameter absent should correspond to defaultValue
).
I like the idea of the table with example behaviour, we should remember to put that in the readme when this stuff works.
This option would be really useful since I would like to read the value from the url initially only if it is supplied.
I had the same requirements (default is store but location should overwrite) and found this thread. Ended up with appropriate initial state + a helper function:
const merge = fn => param => (param ? fn(param) : { type: 'NOOP' });
Then
const storeEnhancer = ReduxQuerySync.enhancer({
params: {
foo: {
selector: state => state.foo,
action: merge(setFoo)
}
},
initialTruth: 'location'
});
You might need another kind of param truthy check.