Search documentation

Search all pages, headings and text, in the current package or topic or in all documentation.

active-state

Framework-independent state for client logic with in-flight work - stores, derived values, effects, operations and lifetimes, with a React adapter.

@workspace/active-state 1.0.1Developmentunreleased, in development in Foundationbaseline ce081dc3

@workspace/active-state keeps client state outside your components. The core has six nouns: Store, Derived, Effect, Operation, Scope and the transaction (transaction()), plus batch and shallow. A React adapter (useStore) and a testing entry sit on top. There is no provider: a model is plain objects you create where you need them.

A store, derived values and useStoreClient

  • Notebook: 4.50

1 item, total 4.50

What it gives you#

  • Simple state stays simple. A Store is a value with setState, update and subscribe. You need none of the other nouns to use it.
  • Derived values with explicit inputs. A Derived declares its inputs and computes from their values. A read is always current, observed or not.
  • Settle, then publish. Subscribers receive (value, previous) after the whole affected graph has settled, once per round, so nobody sees a new input next to a stale derived value.
  • Two boundaries. batch groups notifications and never reverts anything; transaction is synchronous and all-or-nothing, and a reverted transaction notifies nobody.
  • Owned async work. An Operation has a policy (latest-wins, drop-while-running, queue), typed outcomes that never throw, and cancellation. It changes state only through ctx.commit, so a superseded or cancelled call cannot overwrite newer state.
  • Explicit lifetime. A Scope owns stores, effects, operations and child scopes and disposes them in a fixed order.
  • React as an adapter. useStore is useSyncExternalStore with a selection and shallow comparison by default.
  • Testable without React. @workspace/active-state/testing captures errors, records events and drives operations step by step.

What it does not do#

  • No default equality on a store. A Store without equal publishes every write, also an identical value. Pass equal: shallow (or your own) where you rely on value semantics.
  • No persistence, no network, no serialization. Fetching, storage and hydration data are yours; an Operation only owns the life of the work.
  • No React context. useStore reads the object you pass; it never reads a provider. An error in a subscriber or effect goes to the runtime's error sink (reportError), not to a React error boundary.

Entry points#

ImportContents
@workspace/active-statethe framework-independent core; imports no React
@workspace/active-state/reactthe whole core again (the same objects), plus useStore and useScoped
@workspace/active-state/testingtest seams and the instrumentation protocol; internal and unstable, for tests

Pick one of the first two per file: /react repeats the whole core.

What your application provides#

  • React 19 and @types/react, only where you import /react. The core needs nothing.
  • A TypeScript-aware build: the package ships its TypeScript source.
  • The data and the side effects: requests, storage, timers. Put them in an Operation or an Effect so their lifetime is owned.

Dependencies#

None at run time. react is an optional peer, needed only for /react.

Next#

This page as Markdown