Wondering what’s next for npm?Check out our public roadmap! »

statebot-react-hooks

1.2.0 • Public • Published

statebot-react-hooks

React Hooks for Statebot.

  • Statebot is a Finite State Machine library.
  • React is a JavaScript library for building user interfaces.

For a Mithril version, see: statebot-mithril-hooks

Examples

Installation:

npm i react statebot statebot-react-hooks

useStatebot

For hooking-into Statebots that have life-cycles independent of the components that use them, useStatebot:

import React from 'react'

import { Statebot } from 'statebot'
import { useStatebot } from 'statebot-react-hooks'

export const loadMachine = Statebot('loader', {
  chart: `
    idle ->
      waiting ->
      loaded | failed ->
      idle
  `
})

loadMachine.performTransitions(({ emit }) => ({
  'idle -> waiting': {
    on: 'start-loading',
    then: () => {
      // Fail half the time for this demo
      const fail = Math.random() > 0.5
      setTimeout(() => {
        fail ? emit('error') : emit('success')
      }, 1000)
    }
  },
  'waiting -> loaded': {
    on: 'success'
  },
  'waiting -> failed': {
    on: 'error'
  }
}))

const { Enter, Emit, inState } = loadMachine

function LoadingButton() {
  const state = useStatebot(loadMachine)

  return (
    <button
      className={state}
      onClick={Emit('start-loading')}
      disabled={!inState('idle')}
    >
      {inState('idle', 'Load')}
      {inState('waiting', 'Please wait...')}
      {inState('loaded', 'Done!')}
      {inState('failed', 'Whoops!')}
    </button>
  )
}

function ResetButton() {
  return <button onClick={Enter('idle')}>Reset</button>
}

You can play around with this one in a CodeSandbox.

useStatebotFactory

For Statebots whose life-cycles are tied to the components using them, useStatebotFactory:

import React from 'react'
import { useStatebotFactory } from 'statebot-react-hooks'

const CHART = `
  idle ->
    loading -> (loaded | failed) ->
    idle
`

const EVENT = {
  START_LOADING: 'start-loading',
  LOAD_SUCCESS: 'load-success',
  LOAD_ERROR: 'load-error'
}

function LoadingButton (props) {
  const { state, bot } = useStatebotFactory(
    'loading-button',
    {
      chart: CHART,
      startIn: 'idle',
      logLevel: 4,

      performTransitions: ({ Emit }) => ({
        'idle -> loading': {
          on: EVENT.START_LOADING,
          then: () => setTimeout(
            Emit(EVENT.LOAD_SUCCESS),
            1000
          )
        },
        'loading -> loaded': {
          on: EVENT.LOAD_SUCCESS
        },
        'loading -> failed': {
          on: EVENT.LOAD_ERROR
        }
      }),

      onTransitions: () => ({
        'loading -> failed': () => {
          console.log('Oops...')
        }
      })
    }
  )

  return (
    <button
      className={state}
      onClick={bot.Emit(EVENT.START_LOADING)}
      disabled={bot.inState('loading')}
    >
      {bot.inState('idle', 'Load')}
      {bot.inState('loading', 'Please wait...')}
      {bot.inState('loaded', 'Done!')} ({state})
    </button>
  )
})

useStatebotEvent

To hook-into onEvent, onEntering/ed, onExiting/ed, onSwitching/ed with side-effects cleanup, useStatebotEvent:

import React from 'react'
import { Statebot } from 'statebot'
import {
  useStatebot,
  useStatebotEvent
} from 'statebot-react-hooks'

const bot = Statebot('loader', {
  chart: `
    idle ->
      loading -> (loaded | failed) ->
      idle
  `
})

const { Enter, Emit, inState } = bot

function LoadingButton() {
  const state = useStatebot(bot)

  useStatebotEvent(bot, 'onEntered', 'loading', () =>
    setTimeout(
      bot.Emit(EVENT.LOAD_SUCCESS),
      seconds(1)
    )
  )

  // You can achieve the same with useEffect, and you
  // get more control over the dependencies, too:
  useEffect(() => {
    const cleanupFn = bot.onExited('loading', () =>
      setTimeout(
        bot.Enter('idle'),
        seconds(2)
      )
    )
    return cleanupFn
  }, [bot])

  return (
    <button
      className={state}
      onClick={Emit('start-loading')}
      disabled={inState('loading')}
    >
      {inState('idle', 'Load')}
      {inState('loading', 'Please wait...')}
      {inState('loaded', 'Done!')} ({state})
    </button>
  )
}

function seconds(n) {
  return n * 1000
}

Contributing

This is a pretty basic implementation of hooks for Statebot. I don't think much else is needed, but by all means fork and tinker with it as you like.

Of course, please stop-by the Statebot repo itself. :)

License

Statebot was written by Conan Theobald and is MIT licensed.

Install

npm i statebot-react-hooks

DownloadsWeekly Downloads

5

Version

1.2.0

License

MIT

Unpacked Size

48.5 kB

Total Files

16

Last publish

Collaborators

  • avatar