Concept of Stale, Revalidate, & Invalidate
Understanding these concepts is key to how FloppyDisk manages async state efficiently and predictably.
Stale vs Fresh
Every query can be either:
- Fresh โ data is still considered up-to-date
- Stale โ data is considered outdated (need revalidation)
This is controlled by staleTime (default: 2.5 seconds).
The timer starts right after the promise resolves.
Simulation
Let's say a query with { staleTime: 2500 }.
Timeline simulation:
- t = 0ms โ query executed
- t = 500ms โ promise resolved โ data is considered FRESH โ โฑ๏ธ start stale timer
- t = 2500ms โ still FRESH (2000ms since time started)
- t = 3000ms โ becomes STALE (2500ms since time started)
- t = 4000ms โ still STALE
Stale time starts after data is available, not when the request begins. This ensures freshness is based on actual data age, not network duration.
Mental Model
- Fresh = trust the cache
- Stale = safe to revalidate (refetch)
Execute
execute() means:
Always run the promise, no matter what.
- Ignores stale state
- Ignores invalidation
- Forces a fetch
๐ก In TanStack Query terms, execute is equivalent to fetch.
Revalidate
revalidate() is conditional:
- If query is stale โ run the promise
- If query is invalidated โ run the promise
- Otherwise โ do nothing (use cached data)
Revalidation respects freshness.
๐ก In TanStack Query terms, revalidate is equivalent to refetch.
Invalidate
invalidate() marks a query as invalid, even if it's still fresh.
Then:
- If the query is subscribed โ automatically execute
- If the query is unsubscribed โ stop here โ wait until it becomes subscribed, then execute
This ensures:
- No unnecessary work for unused queries
- Active UI always stays up-to-date
๐ก In TanStack Query terms, invalidate is equivalent to invalidateQueries.
Subscribed vs Unsubscribed
Based on total subscribers, a query can be classified into two conditions:
- Subscribed query โ has active subscribers
- Unsubscribed query โ has no subscribers
This distinction is important because it determines whether a query should actively update or stay idle.
If a query is used by a currently rendered component, it becomes subscribed, because the component internally subscribes to the query store.
How They Work Together
| Command | Fresh/Stale | Subscribed | Query function called? |
|---|---|---|---|
execute() | Any | Any | โ Called |
revalidate() | Fresh | Any | โ Not called |
revalidate() | Stale | Any | โ Called |
invalidate() | Any | Yes | โ Called immediately |
invalidate() | Any | No | โณ Deferred โ Called when the query becomes subscribed |