Docs
Concept of Stale, Revalidate, & Invalidate

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

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