@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
anduseDispatch
).
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.
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 );
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.
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.
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
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.
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 APIsuseSelect
/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.
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.