-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: Second parameter for atomFamily passed to default function #231
Comments
Hi @afitiskin Perhaps not as elegant but you can pass more values by using an object or array. With object: const myGenericNumberState = atomFamily({
key: 'MyNumbers',
default: ({x, y}) => defaultBasedOnXY(x, y),
});
myGenericNumberState({x: 1, y: 2}); With array; const myGenericNumberState = atomFamily({
key: 'MyNumbers',
default: ([x, y]) => defaultBasedOnXY(x, y),
});
myGenericNumberState([1, 2]); |
Yes, we use named parameters by passing in objects as the parameter all the time. Please also note the restrictions for types that may be passed into |
@acutmore @drarmstr thank you guys for your answers! Unfortunately passing array or object as a parameter is not an option for my case. I'm working on Let's say we have a form with ability to set a default value for every field. So, for every field we have I may achieve my goal with lodash memoization:
In example above atom will be memoized by name only, and may be accessed by name only:
But with your suggestion this could not be achieved:
|
Another possible solution of my case is to allow to pass serialization function to convert passed object to key manually:
|
Hi @afitiskin, That use case makes sense. Maybe @drarmstr will consider it for Recoil. As you say for now you can build this with your own implementation of AtomFamily that only memoizes based on the first argument. |
@acutmore yep, currently we have our own cache/memoization system, but with release of |
@afitiskin Ah, yes, I see. One parameter for scoping the atom with the key and another just to pass to the default function. Note that you can't just trivially transfer the second param to the internal default selector, since that's also part of the memoized atom. So, when you got the atom a second time, the second parameter would actually be ignored. Instead of using a default selector, you could wrap the atom with a selector. At the end of the day you'd probably end up with a pattern similar to the helper we use. I don't think we published it as part of the utils, but here's the idea: function evaluatingAtom<T, StoredType, P: ?Parameter>({
get = () => (_, value) => value,
set = () => (_, value) => value,
cacheImplementation_UNSTABLE: cacheImplementation,
...opt
}: EvaluatingAtomOptions<T, StoredType, P>): P => RecoilState<T> {
const myAtom = atom<StoredType>(opt);
const mySelector = selectorFamily<T, P>({
key: `${opt.key}__evaluation`,
get: evaluationParams => ({get: getRecoilValue}) =>
get(evaluationParams)({get: getRecoilValue}, getRecoilValue(myAtom)),
set: evaluationParams => (
{set: setRecoilValue, get: getRecoilValue},
newValue,
) =>
setRecoilValue<StoredType>(
myAtom,
newValue instanceof DefaultValue
? newValue
: oldValue =>
set(evaluationParams)({get: getRecoilValue}, newValue, oldValue),
),
cacheImplementation_UNSTABLE:
cacheImplementation && (() => cacheImplementation),
dangerouslyAllowMutability: opt.dangerouslyAllowMutability,
});
return evaluationParams => mySelector(evaluationParams);
}
function evaluatingAtomFamily<
T,
StoredType,
ScopeParameter: Parameter,
EvaluationParameter,
>({
get = () => (_, value): T => value,
set = () => (_, value): StoredType => value,
cacheImplementationForEvaluation_UNSTABLE: cacheImplementationForEvaluation,
...options
}: evaluatingAtomFamilyOptions<
T,
StoredType,
ScopeParameter,
EvaluationParameter,
>): (ScopeParameter, EvaluationParameter) => RecoilState<T> {
const baseAtom: ScopeParameter => RecoilState<StoredType> = atomFamily(
options,
);
// If there are get/set accessors associated with this atomFamily,
// then construct a wrapping selector to perform those evaluations whenever
// the atom is read or written.
const evaluationSelector = selectorFamily<
T,
{scopeParam: ScopeParameter, evalParam: EvaluationParameter},
>({
key: `${options.key}__evaluation`,
get: ({scopeParam, evalParam}) => ({get: getRecoilValue}) =>
get(scopeParam, evalParam)(
{get: getRecoilValue},
getRecoilValue(baseAtom(scopeParam)),
),
set: ({evalParam, scopeParam}) => (
{set: setRecoilValue, get: getRecoilValue},
newValue,
) =>
setRecoilValue(
baseAtom(scopeParam),
newValue instanceof DefaultValue
? newValue
: oldValue =>
set(scopeParam, evalParam)(
{get: getRecoilValue},
newValue,
oldValue,
),
),
cacheImplementation_UNSTABLE: cacheImplementationForEvaluation,
});
return (scopeParam, evalParam) => evaluationSelector({scopeParam, evalParam});
} With this you could use call-site-specific defaults: const fieldInitialValueState = evaluatingAtomFamily({
key: 'Field',
default: undefined,
get: (field, initialValue) => (_, value) => value ?? initialValue,
});
useRecoilValue(fieldInitialValueState('email', '[email protected]')); |
I second this so we can compose more dynamically. Another idea is to allow us to get an atom/selector by key. |
This looks perfect! Any chance we can have this in the next release? I have no idea how to add this to my current project. |
@cybervaldez - you don't need this fancy helper, just wrap a |
@drarmstr hey. I follow the document and split demo: https://codesandbox.io/s/condescending-sinoussi-d3ck0?file=/src/App.js:2020-2035 |
@xuzhanhh - |
@drarmstr I hope stop rerendering |
@xuzhanhh - In your example |
@drarmstr could you please provide a working example to play with, I've looked through the sample code above but I'm not able to fully understand, how it is achieved. BTW awesome library eliminated so many issues I had working with Forms where context had to be updated frequently and always felt hacky. |
Example usage of a parameter used for a default might look something like this: const fieldState = evaluatingAtomFamily({
key: 'MyState',
get: (field, defaultValue) => ({get}, fieldValue) => fieldValue ?? defaultValue,
});
const x = useRecoilValue(fieldState('email', '[email protected]')); |
First off, thanks for this really cool library, it has great use cases! This is proving to be the largest headache in my implementation of this library. I have a need for a dynamic default value for my family of atoms. My use case is a bunch of text editors on a page, each of which have their own content. My only other option is setting my state in a
|
The |
|
First of all, thank you guys for an awesome library!
I checked the latest version (
0.0.8
) and found your utilitiesatomFamily
andselectorFamily
very useful, but currently limited.From your example
default
value for anatom
inatomFamily
may be parameterized:In case above
myAtomKey
is responsible for both: atom key and it's default value. This behaviour makesatomFamily
very limited. What if we add additional parameter to the function which is returned fromatomFamily
and simply pass this additional parameter todefault
function?Now, we may (or may not) pass additional parameters to create atoms with custom default values and this addition will make
atomFamily
more useful. Same additional parameter should be added toselectorFamily
as well:The text was updated successfully, but these errors were encountered: