@wordpress/stan Edit

“stan” stands for “state” in Polish 🇵🇱. It’s a framework agnostic library to manage distributed state in JavaScript application. It is highly inspired by the equivalent Recoil and jotai

It share the same goals as Recoil and Jotai:

  • Based on atoms (or observables) which means it’s highly performant at scale – only what needs to update gets updated.
  • Shares with Jotai the goal of maintaining a very light API surface.
  • Supports async and sync state.

Unlike these frameworks, it has the following goals that justified the creation of a separate library:

  • It is independent from other libraries like React. You can create binding for any of your desired frameworks.
  • It needs to be flexible enough to offer bindings for @wordpress/data consumer API (useSelect and useDispatch).

Installation Installation

Install the module

npm install @wordpress/stan --save

This package assumes that your code will run in an ES2015+ environment. If you’re using an environment that has limited or no support for ES2015+ such as lower versions of IE then using core-js or @babel/polyfill will add support for these methods. Learn more about it in Babel docs.

Top ↑

Getting started Getting started

Stan is based on the concept of “atoms”. Atoms are discrete state units and your application state is a tree of atoms that depend on each other.

Creating basic atoms Creating basic atoms

Let’s create some basic atoms:

import { createAtom } from '@wordpress/stan';

// Creates an atom with an initial value of 1.
// The value can be of any type.
const counter = createAtom( 1 );

Top ↑

Manipulating atoms Manipulating atoms

In this example we created an atom that can hold a counter that starts with 1.
To manipulate we need a registry. The registry is the container of all atom states.

import { createAtomRegistry } from '@wordpress/stan';

const registry = createAtomRegistry();

// Read the counter.
console.log( registry.get( counter ) ); //  prints 1.

// Modify the value of the counter
registry.set( counter, 10 );

console.log( registry.get( counter ) ); //  prints 10.

Top ↑

Subscribing to changes Subscribing to changes

Each atom is an observable to which we can subscribe:

registry.subscribe( counter, () => {
    console.log( registry.get( counter ) );
} );

registry.set( counter, 2 ); // prints 2.
registry.set( counter, 4 ); // prints 4.

Top ↑

Derived atoms Derived atoms

Atoms can also derive their value based on other atoms. We call these “derived atoms”.

import { createAtom, createDerivedAtom } from '@wordpress/stan';

const counter1 = createAtom( 1 );
const counter2 = createAtom( 2 );
const sum = createDerivedAtom(
    ( { get } ) => get( counter1 ) + get( counter2 )
);

In the example above, we create two simple counter atoms and third derived “sum” atom which value is the sum of both counters.

console.log( registry.get( sum ) ); // prints 3.

// Adding a listener automatically triggers the refreshing of the value.
// If the atom has no subscriber, it will only attempt a resolution when initially read. 
// But it won't bother refreshing its value, if any of its dependencies change.
// This property (laziness) is important for performance reasons.
registry.subscribe( sum, () => {
    console.log( registry.get( sum ) );
} );

// This edits counter1, triggering a resolution of sumInstance which triggers the console.log above.
registry.set( counter1, 2 ); // now both counters equal 2 which means sum will print 4.
registry.set( counter1, 4 ); // prints 6

Top ↑

Async derived atoms Async derived atoms

Derived atoms can use async functions to compute their values. They can for instance trigger some REST API call and returns a promise.

const sum2 = createDerivedAtom(
    async ( { get } ) => {
        const val1 = await Promise.resolve( 10 );
        return val1 * get( counter );
    } 
);

The value of async atoms will be equal to null until the resolution function finishes.

Top ↑

Bindings Bindings

It is important to note that stan instance and registries are low-level APIs meant to be used by developers to build bindings for frameworks of their choice. In general, a higher-level API is preferred.

Currently available bindings:

  • @wordpress/data: WordPress data users can continue to use their existing high-level APIs useSelect/useDispatch (selectors and actions) to access the atoms. The selectors are just high-level atoms that can rely on lower-level ones and the actions are just functions that trigger atom setters. The API for @wordpress/data store authors to bridge the gap is still experimental.

Top ↑

API Reference API Reference

# createAtom

Creates a basic atom.

Parameters

  • initialValue T: Initial value in the atom. *
  • config [WPCommonAtomConfig]: Common Atom config.

Returns

  • WPAtom<T>: Created atom.

# createAtomRegistry

Creates a new Atom Registry.

Parameters

  • onAdd RegistryListener:
  • onDelete RegistryListener:

Returns

  • WPAtomRegistry: Atom Registry.

# createAtomSelector

Parameters

  • resolver WPAtomSelectorResolver<T>: Atom resolver.
  • updater WPAtomSelectorUpdater<T>: Atom updater.
  • atomConfig [WPCommonAtomConfig]: Common Atom config.

Returns

  • (unknown type): Atom selector creator.

# createDerivedAtom

Creates a derived atom.

Parameters

  • resolver WPDerivedAtomResolver<T>: Atom resolver.
  • updater WPDerivedAtomUpdater<T>: Atom updater.
  • config [WPCommonAtomConfig]: Common Atom config.

Returns

  • WPAtom<T>: Created atom.

# createStoreAtom

Creates a store atom.

Parameters

  • subscribe (unknown type): Subscribe to state changes.
  • get (unknown type): Get the state value.
  • dispatch (unknown type): Dispatch store changes,
  • config [WPCommonAtomConfig]: Common Atom config.

Returns

  • WPAtom<T>: Store Atom.

# createStoreAtomSelector

Parameters

  • subscribe (unknown type): Subscribe to state changes.
  • get (unknown type): Get the state value.
  • dispatch (unknown type): Dispatch store changes,
  • atomConfig [WPCommonAtomConfig]: Common Atom config.

Returns

  • (unknown type): Atom selector creator.

Code is Poetry.