1.2.2 • Published 4 years ago

stateq-js v1.2.2

Weekly downloads
9
License
MIT
Repository
github
Last release
4 years ago

Stateq JS

npm.io Node version NPM Downloads Minified size License: MIT

Stateq is a very lightweight finite state machine library made in JavaScript. A finite state machine (fsm) is mathematical model with a finite number of different states. The model can only be in one state at a time. Through defined transitions, it can change its state using a fire-and-forget mechanism. Specifically in front-end development, this model can be very beneficial. It is a good way to eliminate potential side-effects like: addition checks, unnecessary re-renders, unwanted effects of state combinations that should not be possible.

Creating a finite state machine

The first step into creating a finite state machine with stateq is by defining a machine configuration. A machine configuration simply consists out of your state-definitions and your starting point, your initial state. In addition, you can define additional information that need to be stored in the machine, the context, Below you can see a simple example.

import { createMachine } from 'stateq-js';

const config = {
  initialState: {
    value: 'initialState',
    context: { ... }
  },
  states: {
    initialState: {
      events: {
        to1: 'state1',
        to2: 'state2',
      }
    },
    state1: {
      events: {
        to2: { ... }
      },
      initialState: { value: 'hierarchyState1' },
      states: {
        hierarchyState1: { ... }
       }
     },
    state2: { ... }
  },
};

const fsm = machine(config);
const currentState = fsm.initialState; // { value, history, context }

Transition to a new state using events

By using the fsm.transition(state, event) method, we can transition from a given state, based on a transition name, to a new state. Each state, including the initialState, is immutable.

const newState = fsm.transition(currentState, 'transition1'); // { value, history, context }

The transitions we define in the configuration of the machine (e.g. to1) are called events. Events can be a string (target name) or an object. When an event is defined as an object, more powerful objects become available.

  • target: the name of the target state, defined as a string;
  • guard: Function(ctx, ev): a guard defines when a transition can fire. When returning a true, the transition can fire;
  • reduce: Function(ctx, ev): a reducer function that should provide back an object. The context of the new state is replaced with this object;
  • actions: [Function(ctx, ev)]: an array of fire-and-forget functions that are executed on transition. They get the machine's context and current event as input.

Hierarchical states

The finite state machine allows for hierarchical states. This means that a state can be a state machine of its own. In the above configuration, state1 is a good example. By defining an initialState and states attribute, hierarchical states are supported. When transitioning, the finite state machine will always look at the the deepest level first, and moving up until it finds an event it can fire.

Note: context can only live on the machine level. Nested context will be ignored.

Wrapping it in a service

A finite state machine is not able to maintain its current state and perform actions based on that. By interpreting the finite state machine, we create a service that is able to do all of this. This is a necessary step for front-end development. In addition, it makes it possible to let different services interact with each other. By using the interpret(fsm, onChange) function, we can wrap a machine into a service. The onChange function will be triggered on each state change.

import { createMachine, interpret } from 'stateq-js';

const fsm = createMachine(config);
const service = interpret(fsm, (state) => { ... });

The onChange can be triggered by one of the actions present in a service:

  • current: current state of the service (value, context and history);
  • send(event: String | [String]): sends an event (or list of events) to the machine, triggering a transition;
  • rollback(): makes it possible to reverse the last transition. This action can only be executed once in-between transitions/sends;
  • reset(): sets the machine back the the initial state.

Similar to the machine itself, the actions of a service can be chained.

service.send('t1').send('t2').rollback()...

Using it in React with hooks

An example on how to use this with React Hooks is shown below.

import { useState, useRef } from 'react';
import { interpret } from 'stateq-js';

export default function useMachine(machine) {
  const [state, setState] = useState(machine.initialState);
  const service = useRef(interpret(machine, setstate));

  return {
    state,
    send: service.current.send,
    rollback: service.current.rollback,
    reset: service.current.reset
  };
}
1.2.2

4 years ago

1.2.1

4 years ago

1.2.0

4 years ago

1.1.3

4 years ago

1.1.2

4 years ago

1.1.1

4 years ago

1.1.0

4 years ago

1.0.0

4 years ago