Closed otakustay closed 3 years ago
Now I introduce a Map
object to store initial values of each particular atom, use a default
option to link atomFamily
and this map:
import {atomFamily, RecoilState} from 'recoil';
interface EntityStore<E, K = string> {
name: string;
initial: Map<K, E>;
family: (key: K) => RecoilState<E>;
}
export function createEntityStore<E, K = string>(name: string): EntityStore<E, K> {
const initial = new Map<K, E>();
return {
name,
initial,
family: atomFamily<E, K>({
key: `entities/${name}`,
default: (key) => initial.get(key),
}),
};
}
I don't think this s a best practice, is there any recommended way to manage entities inside atomFamily
so that we can:
deleted
flag"Deleting" items from an atomFamily()
container can be done using useResetRecoilState()
hook. In your example I don't see that initial
is ever initializing or passing in the default values. I guess it could be set later, but safer to guarantee it's set before the atomFamily()
attempts to reference it for default values. What other issues are you having?
itemsFamily
Item
component for each item key, inside component it tries to get the item via useRecoilValue(itemsFamily(props.id))
hookItem
component can also fetch new value of its owned item and update corresponding atom state via useSetRecoilState(itemsFamily(props.id))
For now I can't figure a good way to archive step 2, I use initial
Map to store initial item values so that a non-existing atom can reference this from Map in step 3
Atoms in an atomFamily()
are created on first-use. So, you can set the value of the atom when parsing the query if you're handling that imperatively, e.g.:
set(itemsFamily(props.id), valueFromQuery);
If you want to hook it up so the items automatically query for their default values and have a pending state while the query is pending, you could use a selectorFamily()
as the default:
// query that returns an object of Item IDs to initial values
const initialItemValuesQuery = selector({
key: 'InitialItemValues',
get: ({get}) => ...fetch object of initial item values...
});
const itemsFamily = atomFamily({
key: 'Items',
default: selectorFamily({
key: 'Items/Default',
get: id => ({get}) => get(initialItemsQuery).id,
}),
});
set(itemsFamily(props.id), valueFromQuery);
How can we implement this set
function? Since useSetRecoilValue
is a hook that cannot be called in loop or async callback, imperative set seems impossible to me
set(itemsFamily(props.id), valueFromQuery);
How can we implement this
set
function? SinceuseSetRecoilValue
is a hook that cannot be called in loop or async callback, imperative set seems impossible to me
You can use set
's in a useRecoilCalback()
in a loop or async.
const setListItems = useRecoilCallback(({set}) => listItems => {
for (const item of listItems) {
set(itemsFamily(item.id), item);
}
});
@drarmstr When I try to set setListItems
like so:
setListItems([{id: 'a'}, {id: 'b'}, {id: 'c'}])
listItems
is a Snapshot object. What am I missing?
@drarmstr When I try to set
setListItems
like so:setListItems([{id: 'a'}, {id: 'b'}, {id: 'c'}])
listItems
is a Snapshot object. What am I missing?
Can you provide more context or a codesandbox.io example?
What seemed to work for me was
const setListItems = useRecoilCallback(({set}) => (listitems) => {
for (const item of listItems) {
set(itemsFamily(item.id, item));
}
});
But was this just a typo above? Or is this a version difference? Or something else?
I'll try and get a code sandbox up, probably tomorrow
Ah, yes, just a typo. Thanks!
In case anyone else gets here from an online search:
I think the code just above still has some typos. This is what worked for me.
const setListItems = useRecoilCallback(({ set }) => (listItems) => {
for (const item of listItems) {
set(itemsFamily(item.id), item);
}
}, []);
Atoms in an
atomFamily()
are created on first-use. So, you can set the value of the atom when parsing the query if you're handling that imperatively, e.g.:set(itemsFamily(props.id), valueFromQuery);
If you want to hook it up so the items automatically query for their default values and have a pending state while the query is pending, you could use a
selectorFamily()
as the default:// query that returns an object of Item IDs to initial values const initialItemValuesQuery = selector({ key: 'InitialItemValues', get: ({get}) => ...fetch object of initial item values... }); const itemsFamily = atomFamily({ key: 'Items', default: selectorFamily({ key: 'Items/Default', get: id => ({get}) => get(initialItemsQuery).id, }), });
I think
get: id => ({get}) => get(initialItemsQuery).id,
should be
get: id => ({get}) => get(initialItemsQuery)[id],
FYI expanded on the previous code sandbox to include the use of atomFamily
to implement the example according to this comment
I was recently discovering recoil to manage an entity store like redux with normalization, I think
atomFamily
is a best to manage entities identified by a unique key:I then encountered an issue where I have already fetched a list of todos and want to put them into this atom family:
Although I can fallback to use a single atom to store all todos and use
selectorFamily
to reach each todo item, its performance can't satisfy me.