Minimal State Machine Hook For React | Reactscript

Minimal State Machine Hook For React | Reactscript

Reactscript - A tiny yet full-featured state machine hook for React. 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...)

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

Examples

  • Examples Walkthrough video:

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'] }

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 three properties: value, nextEvents and context.

value returns the name of the current state. nextEvents returns an array with the names of available transitions from this state.

Send events

send takes a transition name as argument. If the transition exists 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: {},
  },
});

Transition Syntax

For each state, you can define the possible transitions.

Transitions can be configured using a shorthand syntax:

on: {
  TOGGLE: 'active';
}

Or 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() {
        console.log('Just entered the Active state');
        return () => console.log('Just Left the Active state');
      },
    },
  },
});

The effect function receives two params: the send method (So you can trigger transitions from within an effect) and and updater function, to update the context (more on context below).

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) {
            // Return a boolean to allow or block the transition
          },
        },
      },
    },
    active: {
      on: { TOGGLE: 'inactive' },
    },
  },
});

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 update function within your effects to change the context:

const [state, send] = useStateMachine({ toggleCount: 0 })({
  initial: 'idle',
  states: {
    inactive: {
      on: { TOGGLE: 'active' },
    },
    active: {
      on: { TOGGLE: 'inactive' },
      effect(send, update) {
        update(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'] }

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: 'idle',
  states: {
    inactive: {
      on: { TOGGLE: 'active' },
    },
    active: {
      on: { TOGGLE: 'inactive' },
      effect(send, update) {
        update(context => ({ toggleCount: context.toggleCount + 1 }));
      },
    },
  },
});

Wiki

react reactscript

What is Geek Coin

What is GeekCash, Geek Token

Best Visual Studio Code Themes of 2021

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

How native is React Native? | React Native vs Native App Development

Article covers: How native is react native?, React Native vs (Ionic, Cordova), Similarities and difference between React Native and Native App Development.

How to Select and Hire the Best React JS and React Native Developers?

Hire React JS developer from Technoduce, we have a team of experienced and expert Angular JS developer. With our hire React JS developer, you have complete control over the technical resource throughout the development process.

What are hooks in React JS? - INFO AT ONE

In this article, you will learn what are hooks in React JS? and when to use react hooks? Also, we will see the react hooks example.

Hire India's Best React JS Developers| Hire React JS Development Company

Looking to hire top dedicated Reactjs developers in India at affordable prices? Our 5+ years of average experienced Reactjs developers comprise proficiency in delivering the most complex and challenging web apps. Hire ReactJS development...

Build a simple React Native Pokemon app with React-Navigation

As we start learning new technologies we want to start building something or work on a simple project to get a better understanding of the technology.