@statekit/decorations
v0.0.2-dev
Published
Create decorations: config wrappers that add reusable behavior to StateKit machines
Readme
@statekit/decorations
Reusable behavior for StateKit machines. A decoration takes a machine config and returns it with more events and selectors.
Work in progress.
Install
npm i @statekit/core @statekit/decorationsQuick start
import { createMachine, createMachineConfig } from "@statekit/core"
import { createDecoration, fn, slot } from "@statekit/decorations"
type User = { name: string }
const withLoad = createDecoration({
name: "withLoad",
options: {
load: fn<() => Promise<User>>(),
into: slot.data<User | null>(),
loading: slot.state("loading"),
success: slot.state("ready", "success"),
},
events: {
load: async ({ options }) => {
options.loading.enter()
const user = await options.load()
options.into.set(user)
options.loading.exit()
options.success.enter()
},
},
})
const userConfig = createMachineConfig({
states: ["idle", "loading", "ready"],
data: { user: null as User | null },
})
const userMachine = createMachine(
withLoad(userConfig, { load: getUser, into: (d) => d.user })
)
await userMachine.event.load()
userMachine.data.user // { name: "Ada" }The decoration adds events and selectors, never data or states. The machine keeps the shape its config declares.
Options
| Option | Where it's used | In the event |
| ---------------------- | --------------------------------------------------------------------------- | ----------------------------- |
| fn<F>() | Has to be set | The function |
| A plain value | Optional, keeps the default's type | The value |
| slot.data<T>() | Has to be set: (d) => d.user, or (d, id) => d.users[id] for event(id) | get(), set(value), path |
| slot.field<T>(names) | Optional: (d) => d.error. Defaults to the first top level field named so | get(), set(value), path |
| slot.state(names) | Optional: ({ S }) => S.done. Defaults to the first state named so | enter(), exit(), is() |
A slot that finds no field or state does nothing, so a machine without a failed state skips failing.
Mistakes are type errors: a missing function or data slot, a field that can't hold what the decoration writes, or a state the machine doesn't have.
Events
Each event gets { options, data, id }, then its arguments. data is a copy of the machine's data when it's read, and it stays after the event ends. id is the machine's, for what comes after the event: _STATEKIT_.COMPANY.cp.getMachine(id) is the machine while a component uses it. Slots point by the event's arguments. selectors are set next to events and get the same, with slots that only read.
When the config already has an event with the same name, both run, the config's first. The same goes for two decorations on one machine. A selector with the same name replaces the earlier one.
