redux-query-sync icon indicating copy to clipboard operation
redux-query-sync copied to clipboard

initialTruth: 'merge'

Open Treora opened this issue 6 years ago • 4 comments

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 defaultValues 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)

Treora avatar Jul 12 '17 12:07 Treora

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}

leonelgalan avatar Nov 15 '17 21:11 leonelgalan

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.

Treora avatar Nov 16 '17 17:11 Treora

This option would be really useful since I would like to read the value from the url initially only if it is supplied.

jsherin avatar Feb 05 '18 17:02 jsherin

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.

peterporfy avatar Mar 20 '18 11:03 peterporfy