Recoil icon indicating copy to clipboard operation
Recoil copied to clipboard

`recoil-sync` & `refine`: return `DefaultValue` if provided `queryParam` is invalid

Open nichita-pasecinic opened this issue 1 year ago • 5 comments

I was looking for a effect that would attempt to do it, but could not implement one so it would sync correctly with atom state. Use case:

  • User enters an invalid query param
  • refine throws a runtime error

Is there any possibility to return the DefaultValue for the atom (in case of a invalid start from user, that would not pass refine checker) and after that sync the atoms' query param with the DefaultValue value ?

nichita-pasecinic avatar Aug 29 '22 12:08 nichita-pasecinic

Sorry, I'm not quite following the example for this request?

drarmstr avatar Aug 29 '22 22:08 drarmstr

@drarmstr sure, I will explain in another way.

For example we have an atom that is in sync with query param param

export const myParamAtom= atom<number>({
  key: 'myParamAtom',
  default: 0,
  effects: [
    urlSyncEffect({
      storeKey: 'queryParamsStore',
      refine: number(),
      itemKey: 'param',
    }),
  ],
});

When use would access the link: https://some-domain.com?param="1", refine would throw a runtime error as "1" is not of type number. I would like to handle it differently, not to throw a error but in case is a invalid value, I would return DefaultValue and replace the invalid query param value in the url with the applied default value.

urlSyncEffect({
   storeKey: 'queryParamsStore',
    refine: number(),
    itemKey: 'param',
    read: ({ read }) => {
      const value = read('param');
      const result = number(value );
      if (result.type === 'failure') return new DefaultValue(); // <--- this would return the DefaultValue but in browser URL user will still see the old query param value
      return result.value;
    },
  }),

So, is it possible not to throw runtime errors with refine checkers in case of invalid values, but to return the atom default value and replace the query param with the same default value.

P.S.: this is a pretty common thing, when users some filter in brower url and is easier for him to change a query param value rather than browsing thru UI and find the right filter

nichita-pasecinic avatar Aug 30 '22 06:08 nichita-pasecinic

The read() callback can return new DefaultValue(), as you point out. There is actually also an undocumented actionOnFailure_UNSTABLE option with values errorState or defaultValue, which might be of interest to avoid the custom read()?

But, it sounds like your primary request here is to then update the URL. There is an option syncDefault: true which is intended to also sync the initial default value back to the URL. Does this work for your case?

drarmstr avatar Sep 02 '22 00:09 drarmstr

@drarmstr Thx for your answer, using syncDefault: true works, but this is not a good workaround as it would always add the query param defaults which are not usually needed. If user will try to access https://domain.com/home, I want him to see https://domain.com/home and not https://domain.com/home?param1="default2"&param2="default2"

Unfortunately, I could not get actionOnFailure_UNSTABLE to work, it does not even log to the console, so I could not track what is happening inside it

urlSyncEffect({
      storeKey: 'queryParamsStore',
      refine: number(),
      itemKey: param.queryParamKey,
      // @ts-ignore
      actionOnFailure_UNSTABLE: (args) => {
         console.log('action on failure args: ', args); // <-- this is not getting logged when passing an invalid JSON into the url (e.g.:  `?param1="def`, but at least there is no runtime error thrown xD
      },
      read: ({ read }) => {
        const queryParamValue = read(param.queryParamKey);
        const result = myChecker(param.validValues)(queryParamValue);
        if (result.type === 'failure') return new DefaultValue();
        return result.value;
      },
    }),

same effect but without actionOnFailure_UNSTABLE option applied, produces a runtime error, while the above effect does not produce a runtime error (I assume because it returns undefined) and the atom value is set to DefaultValue, but in browser url we still get the same invalid input (with param that could not be JSON parsed ?param1="def)

nichita-pasecinic avatar Sep 02 '22 19:09 nichita-pasecinic

actionOnFailure_UNSTABLE actually takes a string literal of errorState or defaultValue.

drarmstr avatar Sep 17 '22 02:09 drarmstr