Recoil
Recoil copied to clipboard
`recoil-sync` & `refine`: return `DefaultValue` if provided `queryParam` is invalid
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 ?
Sorry, I'm not quite following the example for this request?
@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
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 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"¶m2="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
)
actionOnFailure_UNSTABLE
actually takes a string literal of errorState
or defaultValue
.