pbeshai / use-query-params

React Hook for managing state in URL query parameters with easy serialization.
https://pbeshai.github.io/use-query-params
ISC License
2.15k stars 96 forks source link

Update defaultValues using withDefault #191

Open sureshgogu-everest opened 2 years ago

sureshgogu-everest commented 2 years ago

Hi, Thanks for the library. We are using use-query-params library in one of our projects and it is working well.

But we are unable to handle a special case with defaultValues.

let's assume we have an baseConfig at global level with following default values

const baseConfig = {
    key1: withDefault(DelimitedNumericArrayParam, [defaultValeus]),
    key2: NumberParam,
    key3: StringParam
}

Somewhere in the component(s) based on the requirement if we want override the default values

Seems we can do this manually again by resetting property

const newConfig = {
    ...baseConfig,
    key1: withDefault(DelimitedNumericArrayParam, latestDefaultValues)
}

Instead of updating for each key, What if we want to update over map to generate new config? We have tried something like this by applying withDefault again on config but it is taking the old default values as it is already configured. PFA

return _.mapValues(baseConfig, function(config, key) {
    if (condition) {
        //here thinking like if we can get the configType with initial decode method instead of decodeWithDefault will solves it
        return withDefault(config[key], newDefaultValue);
    }
    return config;
})
decodeWithDefault

Can you please let us know whether it is possible or not? Did we miss anything if it is already handled?

Thanks in Advance

pbeshai commented 2 years ago

You're essentially doing withDefault(withDefault(DelimitedNumericArrayParam, [defaultValue]), newDefaultValue)

withDefault() internally first decodes with the param type and if undefined, provides the default value. In this case, it will always return [defaultValue] then and never return undefined. You need to keep track of the unwrapped param for your use case (i.e. DelimitedNumericArrayParam in this case).

The code for withDefault is relatively simple. You may have better luck writing your own modifier for your usecase. Note a param is just an object { encode, decode } (and maybe equals), so you just have to produce something with that shape.

pbeshai commented 2 years ago

You could modify withDefault to look at a defaultValue on the param object instead and then mutate that value, e.g. something like (untested):

 function withStatefulDefault<D, DefaultType extends D2, D2 = D>(
  param: QueryParamConfig<D, D2>,
  defaultValue: DefaultType,
  includeNull: boolean = true
): QueryParamConfig<D, any | DefaultType> {
  const paramWithDefault = {...param, defaultValue };

  const decodeWithDefault = (
    ...args: Parameters<typeof param.decode>
  ): Exclude<D2, null | undefined> | Exclude<D2, undefined> | DefaultType => {
    const decodedValue = param.decode(...args);

    if (decodedValue === undefined) {
      return paramWithDefault.defaultValue;
    }
    if (includeNull) {
      if (decodedValue === null) {
        return paramWithDefault.defaultValue;
      } else {
        return decodedValue as Exclude<D2, undefined>;
      }
    }

    return decodedValue as Exclude<D2, undefined | null>;
  };

  paramWithDefault.decode = decodeWithDefault;
  return paramWithDefault;
}

Then you'd do

return _.mapValues(baseConfig, function(config, key) {
    if (condition) {
        // is this a param that is wrapped with withStatefulDefault
        if (config[key].defaultValue != null) {
          // update the param's internal default value and return the param
          config[key].defaultValue = newDefaultValue
          return config[key]
        }
        // if first time, setup this way
        return withStatefulDefault(config[key], newDefaultValue);
    }
    return config;
})

I haven't tested any of this.... and it might not work given it mutates instead of creates a new param (which may break some caching internally, not sure off hand) but it's an idea. hopefully it helps in some way