Concept of Stale, Revalidate, & Invalidate
Understanding these concepts is key to how YuuState 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 |