Skip to content

Derived State

A core principle of robust state management is the Single Source of Truth. You should not store data that can be computed from other data. Storing redundant blocks of state leads to synchronization bugs.

Derived state ensures that your data is always consistent by automatically recalculating values whenever the underlying dependencies change.

Intrinsic Computation

When a computed value belongs logically to a specific object, use standard JavaScript Getters. This keeps the data and its computation encapsulated together.

This is the most common form of derivation in Anchor.

ts
import { mutable } from '@anchorlib/react';

const cart = mutable({
  price: 10,
  quantity: 2,
  
  // The 'total' is a property of the cart, derived from its other properties.
  get total() {
    return this.price * this.quantity;
  }
});

console.log(cart.total); // 20
ts
import { mutable } from '@anchorlib/solid';

const cart = mutable({
  price: 10,
  quantity: 2,
  
  // The 'total' is a property of the cart, derived from its other properties.
  get total() {
    return this.price * this.quantity;
  }
});

console.log(cart.total); // 20

Composite Computation

Sometimes, a value depends on multiple separate state sources that do not share a common parent object. Or, you may need to transform data for a specific UI view (like a View Model) without modifying the original domain object.

In these cases, you define a Reactive Computation that combines these sources.

ts
import { mutable, derived } from '@anchorlib/react';

const todos = mutable([{ text: 'Buy milk', done: false }]);
const filter = mutable('SHOW_ALL');

// This value is computed from two independent sources: 'todos' and 'filter'
const visibleTodos = derived(() => {
  if (filter.value === 'SHOW_COMPLETED') return todos.filter(t => t.done);
  return todos;
});
ts
import { mutable, derived } from '@anchorlib/solid';

const todos = mutable([{ text: 'Buy milk', done: false }]);
const filter = mutable('SHOW_ALL');

// This value is computed from two independent sources: 'todos' and 'filter'
const visibleTodos = derived(() => {
  if (filter.value === 'SHOW_COMPLETED') return todos.filter(t => t.done);
  return todos;
});

Characteristics

  • Automatic Dependency Tracking: The system automatically detects which state properties are accessed during computation. You do not need dependency arrays.
  • Read-Only: Derived values flow one way (Data -> View). You cannot manually assign a value to a derived property.
  • Lazy Evaluation: Computations are optimized to only re-run when necessary.

Reactive Sorting (ordered)

When maintaining sorted lists in a reactive system, calling array.sort() on every update triggers a full O(N log N) re-evaluation. The ordered() primitive solves this by maintaining a reactive sorted view using binary search insertion. When the source array updates, it computes the exact index to insert the new items in O(log N) time, preventing expensive full array re-sorts.

typescript
import { mutable, ordered } from '@anchorlib/core';

const state = mutable({
  movies: [{ title: 'Zoolander' }, { title: 'Alien' }]
});

// Creates a reactive, read-only sorted view
// It automatically updates whenever the source array changes
const sortedMovies = ordered(state.movies, (a, b) => a.title.localeCompare(b.title));

console.log(sortedMovies); // [{ title: 'Alien' }, { title: 'Zoolander' }]

Choosing an Approach

PatternImplementationBest For
IntrinsicJavaScript GetterDomain logic (User.fullName) and encapsulation.
Compositederived() functionCombining separate states (Search + List) or View Models.
Sorted Viewordered() functionMaintaining a reactive sorted list without mutating the source.