I'm trying to implement caching: If a value doesn't exist, then fetch it.
const cache = atom({
key: "cache",
default: new Map()
});
const value = selectorFamily({
key: "value",
get: (id) => async ({ get }) => {
const map = get(cache);
return map.get(id) || await fetch(id);
},
});
My questions are:
1) Can I force a re-fetch without manually fetching and setting in my component? I tried adding a setter:
set: (id) => async ({ set }) => {
const data = await fetch(id);
set(cache, (prevState) => prevState.set(id, data));
},
with the idea of just calling useResetRecoilState(value(id)). But, this doesn't seem to update my components. Is this because set is async?
2) How do I initially populate the cache? I can call set for the IDs I care about, but ideally I want get to add values to the cache.
JS Map is mutable, so you should not call set() on a Map stored in an atom. Atom state isn't really meant to be used for holding caches like this. It looks like you're trying to use a cache to cache the results of the selectorFamily() evaluation? If so, that shouldn't be necessary since Recoil already caches selector evaluations.
Ok, I can replace the Map with an object.
I want to cache because when the selectorFamily is evaluating, it returns a promise. I'd like to return the previous value if available:
Maybe I need to use another atom?
Using a mutable object instead of a map is still a problem. All values stored in an atom should be immutable / read-only. It also sounds like selectors are already doing the caching that you want. When a selector is evaluated for the first time it runs the evaluation function, which may return a pending promise, and the result is cached. Subsequent attempts to get the selector value will then use that same cached promise which may still be pending, or the cache will be updated when the promise resolves or errors.
What about using an immutable map?
The selector is doing _almost_ what I want. My issue is that my selector is impure - first eval returns 1, next eval may return 2, etc. I need something to store the resolved 1 value and provide it to consumers, while fetching 2. When 2 resolves, it will overwrite 1.
Duplicate of #290
Ah, so your usecase is to return the previous value while pending. the useTransition() hook may be useful for this when Recoil releases full concurrent mode support. In the meantime, #290 discusses some current workarounds.