What's the best way to cache a selectors return value until a certain timeout #1728
-
I apologize if i'm over thinking this, I'd like to have a selector based off async dependency ( imagine some database query ) that when it's used by a component, it returns the cached valued until a certain amount of time (let's say 5 seconds) has passed since the last request to the async dependency.
I know in theory I could just be constantly telling this selector to refresh ever 5 seconds with a setInterval, but i was wondering if there was some more elegant way to cause a selector to refresh only if it ends up being used by a component. Is there some way for a selector to effectively say "Okay, here's a value for now, but next time you depend on me for a value again, I might have something different for you". Is this something that could be done with Effects? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
Well, refreshing a selector just clears the cache. It will only actually re-evaluate if it is used. Of course, clearing the cache with components depending on the selector will cause them to re-render and re-evaluate. Lots of other options, I guess. Does it need to be a selector? A hook could check the current timestamp and see if it needs to re-query a cached value. Or you could use an atom with an effect to clear itself every 5s and consumers of the query check that state also to see if they need to re-query. But, in general, if you are updating any selector or atom state Recoil will try to keep that state in sync with components. If you're trying to create a lazy cache that may be inconsistent with rendered state, then you may need to implement your own memoization. |
Beta Was this translation helpful? Give feedback.
-
I ended up doing something like this! I did a selector. Thanks for the info on refreshing just clearing the cache and not immediately evaluating. import { differenceInMilliseconds, secondsToMilliseconds } from "date-fns";
import { useEffect } from "react";
import { useSetRecoilState } from "recoil";
import { fetchPokemon } from "../client/pokemonClient";
import { pokemonState } from "../state/pokemon";
let lastRetrieved: Date | undefined;
export function usePokemon() {
const setPokemon = useSetRecoilState(pokemonState);
useEffect(() => {
loadPokemon();
}, []);
if (lastRetrieved) {
const difference = differenceInMilliseconds(new Date(), lastRetrieved);
if (difference > secondsToMilliseconds(5)) {
console.log("cache has been used more than 5 seconds, reload");
loadPokemon();
}
}
async function loadPokemon() {
lastRetrieved = new Date();
setPokemon(await fetchPokemon());
console.log("fetched");
}
return {
loadPokemon,
};
} |
Beta Was this translation helpful? Give feedback.
I ended up doing something like this! I did a selector. Thanks for the info on refreshing just clearing the cache and not immediately evaluating.