Giter Site home page Giter Site logo

usestatemachine's Introduction

The ½ kb state machine hook for React:

  • Feature complete (Entry/exit callbacks, Guarded transitions & Extended State - Context)
  • Heavy focus on type inference (you get auto completion for both TypeScript & JavaScript users without having to manually define the typings)
  • Idiomatic React patterns (Since it's built on top of React's useReducer & useEffect, might as well...)

size_badge

This docs are for the 1.0.0 version (currently in beta). Older 0.x.x docs

Examples

Installation

$ npm install @cassiozen/usestatemachine

Sample Usage

const [state, send] = useStateMachine()({
  initial: 'inactive',
  states: {
    inactive: {
      on: { TOGGLE: 'active' },
    },
    active: {
      on: { TOGGLE: 'inactive' },
      effect() {
        console.log('Just entered the Active state');
        // Same cleanup pattern as `useEffect`:
        // If you return a function, it will run when exiting the state.
        return () => console.log('Just Left the Active state');
      },
    },
  },
});

console.log(state); // { value: 'inactive', nextEvents: ['TOGGLE'] }

// Refers to the TOGGLE event name for the state we are currently in.

send('TOGGLE');

// Logs: Just entered the Active state

console.log(state); // { value: 'active', nextEvents: ['TOGGLE'] }

What's up with the double parenthesis?

useStateMachine is a curried function (Yummm tasty!) because TypeScript doesn't yet support partial generics type inference. This workaround allows TypeScript developers to provide a custom type for the context while still having TypeScript infer all the types used in the configuration (Like the state & transitions names, etc...).

API

useStateMachine

const [state, send] = useStateMachine(/* Optional Context */)(/* Configuration */);

useStateMachine takes a JavaScript object as context (optional, see below) and one as the state machine configuration. It returns an array consisting of a current machine state object and a send function to trigger transitions.

Machine state

The state consists of 4 properties: value, event, nextEvents and context.

value (string): Returns the name of the current state.

event (eventObject: {type: string}; Optional): The name of the last sent event that led to this state.

nextEvents (string[]): An array with the names of available events to trigger transitions from this state.

context: The state machine extended state. See "Extended State" below.

Send events

send takes an event as argument, provided in shorthand string format (e.g. "TOGGLE") or as an event object (e.g. { type: "TOGGLE" })

If the transition exists in the configuration object for that state, and is allowed (see guard), it will change the state machine state and execute effects.

State Machine configuration

The configuration object should contain:

  • initial: The initial state node this machine should be in
  • verbose(optional): If true, will log every context & state changes. Log messages will be stripped out in the production build.
  • states: Define each of the possible states:
const [state, send] = useStateMachine()({
  initial: 'inactive',
  verbose: true,
  states: {
    inactive: {},
    active: {},
  },
});

Events & Transition Syntax

A state transition defines what the next state is, given the current state and event. State transitions are defined on state nodes, in the on property:

on: {
  TOGGLE: 'active';
}

// (Where TOGGLE stands for an event name that will trigger a transition.)

Or using the extended, object syntax, which allows for more control over the transition (like adding guards):

on: {
  TOGGLE: {
    target: 'active',
  },
};

Effects (entry/exit callbacks)

Effects are triggered when the state machine enters a given state. If you return a function from your effect, it will be invoked when leaving that state (similarly to how useEffect works in React).

const [state, send] = useStateMachine()({
  initial: 'active',
  states: {
    active: {
      on: { TOGGLE: 'inactive' },
      effect({ send, setContext, event, context }) {
        console.log('Just entered the Active state');
        return () => console.log('Just Left the Active state');
      },
    },
  },
});

The effect function receives an object as parameter with four keys:

  • send: Takes an event as argument, provided in shorthand string format (e.g. "TOGGLE") or as an event object (e.g. { type: "TOGGLE" })
  • setContext: Takes an updater function as parameter to set a new context (more on context below). Returns an object with send, so you can set the context and send an event on a single line.
  • event: The event that triggered a transition to this state. (The event parameter always uses the object format (e.g. { type: 'TOGGLE' }).).
  • context The context at the time the effect runs.

In this example, the state machine will always send the "RETRY" event when entering the error state:

const [state, send] = useStateMachine()({
  initial: 'loading',
  states: {
    /* Other states here... */
    error: {
      on: {
        RETRY: 'load',
      },
      effect({ send }) {
        send('RETRY');
      },
    },
  },
});

Guards

You can set up a guard per transition, using the transition object syntax. Guard run before actually running the transition: If the guard returns false the transition will be denied.

const [state, send] = useStateMachine()({
  initial: 'inactive',
  states: {
    inactive: {
      on: {
        TOGGLE: {
          target: 'active',
          guard({ context, event }) {
            // Return a boolean to allow or block the transition
          },
        },
      },
    },
    active: {
      on: { TOGGLE: 'inactive' },
    },
  },
});

The guard function receives an object with the current context and the event. The event parameter always uses the object format (e.g. { type: 'TOGGLE' }).

Extended state (context)

Besides the finite number of states, the state machine can have extended state (known as context).

You can provide the initial context value as the first argument to the State Machine hook, and use the setContext function within your effects to change the context:

const [state, send] = useStateMachine({ toggleCount: 0 })({
  initial: 'inactive',
  states: {
    inactive: {
      on: { TOGGLE: 'active' },
    },
    active: {
      on: { TOGGLE: 'inactive' },
      effect({ setContext }) {
        setContext(context => ({ toggleCount: context.toggleCount + 1 }));
      },
    },
  },
});

console.log(state); // { context: { toggleCount: 0 }, value: 'inactive', nextEvents: ['TOGGLE'] }

send('TOGGLE');

console.log(state); // { context: { toggleCount: 1 }, value: 'active', nextEvents: ['TOGGLE'] }

Context Typing

The context types are inferred automatically in TypeScript, but you can provide you own typing if you want to be more specific:

const [state, send] = useStateMachine<{ toggleCount: number }>({ toggleCount: 0 })({
  initial: 'inactive',
  states: {
    inactive: {
      on: { TOGGLE: 'active' },
    },
    active: {
      on: { TOGGLE: 'inactive' },
      effect({ setContext }) {
        setContext(context => ({ toggleCount: context.toggleCount + 1 }));
      },
    },
  },
});

Wiki

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Cassio Zen

💻 📖 ⚠️ 🤔 🐛

Michael Schmidt

💻 ⚠️ 🤔

Joseph

💻

Jeremy Mack

📖

Ron

📖

Klaus Breyer

📖

Arthur Denner

💻 🐛 ⚠️ 🤔

This project follows the all-contributors specification. Contributions of any kind welcome!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.