internal-docs/guides/reactivity/laws.md
"A reactive abstraction must provide both the current value and a means to detect invalidation without recomputation."
From the perspective of a Glimmer user, this axiom enables writing reactive code using standard JavaScript functions and getters that automatically reflect the current state of UI inputs.
Glimmer users write UI code as straightforward rendering functions, yet the system behaves as if these functions re-execute completely whenever any reactive value changes.
[!IMPORTANT]
When root state is mutated, all reactive abstractions reflect those changes immediately, even when implemented with caching. Glimmer's reactive values are always coherent — changes are never batched in ways that would allow inconsistencies between computed values and their underlying root state.
cell API, but containers from tracked-builtins and the storage created by
the @tracked decorator are also root reactive state.In order to satisfy the Fundamental Axiom of Reactivity, all reactive abstractions must adhere to these six laws:
Dependency Tracking: A reactive abstraction must invalidate when any reactive values used in its last computation have changed. The revision of the tag associated with the reactive abstraction <u>must</u> advance to match the revision of its most recently updated member.
Value Coherence: A reactive abstraction must never return a cached value from a revision older than its current revision. After a root state update, any dependent reactive abstractions must recompute their value when next snapshotted.
Transactional Consistency: During a single rendering transaction, a reactive abstraction must return the same value and revision for all snapshots taken within that transaction.
Snapshot Immutability: The act of snapshotting a reactive abstraction must not advance the reactive timeline. Recursive snapshotting (akin to functional composition) naturally involves tag consumption, yet remains consistent with this requirement as immutability applies recursively to each snapshot operation.
Defined Granularity: A reactive abstraction must define a contract specifying its invalidation granularity, and must not invalidate more frequently than this contract permits. When a reactive abstraction allows value mutations, it must specify its equivalence comparison method. When a new value is equivalent to the previous value, the abstraction must not invalidate.
All reactive abstractions—including built-in mechanisms like @tracked and createCache, existing
libraries such as tracked-toolbox and tracked-builtins, and new primitives like cell—must
satisfy these six laws to maintain the Fundamental Axiom of Reactivity when these abstractions are
composed together.
[!TIP]
In practice, the effectiveness of reactive composition is bounded by the Defined Granularity and Specified Equivalence of the underlying abstractions.
For instance, if a
cellimplementation defines granularity at the level of JSON serialization equality, then all higher-level abstractions built upon it will inherit this same granularity constraint.The laws do not mandate comparing every value in every computation, nor do they require a uniform approach to equivalence based solely on reference equality. Each abstraction defines its own appropriate granularity and equivalence parameters.
For developers building reactive abstractions, carefully selecting granularity and equivalence specifications that align with user mental models is crucial—users will experience the system through these decisions, expecting UI updates that accurately reflect meaningful changes in their application state.