Docs
Concept of Stale, Revalidate, & Invalidate

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

CommandFresh/StaleSubscribedQuery function called?
execute()AnyAny✅ Called
revalidate()FreshAny❌ Not called
revalidate()StaleAny✅ Called
invalidate()AnyYes✅ Called immediately
invalidate()AnyNo⏳ Deferred
→ Called when the query becomes subscribed