Skip to main content

Caching

As Albert Einstein once said, there are only two hard problems in computer science:

  • Naming things
  • Cache invalidation
  • Off-by-one errors

I'm not entirely sure what he meant, but let's talk about caching. Unlike Jotai, which doesn't really care about caching out of the box, or Recoil, which caches very aggressively until you opt out, Stan takes a more balanced approach - somewhere between the two. Below, you'll find a rundown of all layers of Stan's cache.

Store

Every state change in Stan occurs within the context of a store (see Scoped<T>). You can think of it as each piece of state being cached using the Store instance as the cache key. A state change scoped to a specific store exists only within that store - which can, in fact, be leveraged to your advantage (see switching stores).

atom

Once initialized, the atom's value is cached. Until it is updated or reset, subscribers won't be notified, and all read attempts will return the cached value.

info

The atom's lifecycle is heavily influenced by the areValuesEqual setting (see the atom documentation). If the new value is considered equal, the cache entry is not replaced, and no change is propagated.

atomFamily

The atomFamily helper returns a memoized function that maps serializable parameters to atoms. For several reasons (e.g., avoiding re-running atom effects), the cache size is not limited. Every new parameter - or more precisely, every parameter that serializes to a different string - adds a new atom to the cache. There is no difference between "standalone" atoms and those produced by atomFamily - the same caching rules apply to both.

selector

The caching rules for a selector are somewhat similar to those of an atom. Once initialized (when the selector function is first evaluated), the returned value is stored in the cache and remains there until it either changes or the selector is refreshed. The difference is what triggers a selector change - it can only happen when one of its dependencies changes (dependencies are consumed via get calls).

info

The selector's lifecycle is heavily influenced by the areValuesEqual setting (see the selector documentation). If the new value is considered equal, the cache entry is not replaced, and no change is propagated.

selectorFamily

The selectorFamily helper returns a memoized function that maps serializable parameters to selectors. Unlike atomFamily, it allows configuring cache behavior via the cachePolicy setting (see the selectorFamily documentation). Once a selector is removed from the cache (e.g., due to hitting the size limit), it's as if it never existed. There is no difference between "standalone" selectors and those produced by selectorFamily - the same caching rules apply to both.