All Projects → MicheleBertoli → React Automata

MicheleBertoli / React Automata

Licence: mit
A state machine abstraction for React

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to React Automata

Rust fsm macros
FSM in Rust's macros.
Stars: ✭ 20 (-98.48%)
Mutual labels:  state-machine
Trck
Query engine for TrailDB
Stars: ✭ 48 (-96.35%)
Mutual labels:  state-machine
Xstateful
A wrapper for xstate that stores state, handles transitions, emits events for state changes and actions/activities, and includes an optional reducer framework for updating state and invoking side-effects
Stars: ✭ 81 (-93.84%)
Mutual labels:  state-machine
Mineflayer Statemachine
A state machine plugin for Mineflayer to aid in designing more complex behavior trees.
Stars: ✭ 32 (-97.57%)
Mutual labels:  state-machine
Formatwith
String extensions for named parameterized string formatting.
Stars: ✭ 44 (-96.66%)
Mutual labels:  state-machine
Aws Power Tuner Ui
AWS Lambda Power Tuner UI is an open source project creating a deployable easy to use website built on a layered technology stack allowing you to optimize your Lambda functions for cost and/or performance in a data-driven way via an easy to use UI.
Stars: ✭ 52 (-96.05%)
Mutual labels:  state-machine
Cycle State Machine Demo
Non-trivial, real use case demo of a hierarchical state machine library with cyclejs
Stars: ✭ 25 (-98.1%)
Mutual labels:  state-machine
Jstate
Advanced state machines in Java.
Stars: ✭ 84 (-93.62%)
Mutual labels:  state-machine
Yii2 Lifecycle Behavior
Define the lifecycle of a model by defining allowed status changes.
Stars: ✭ 47 (-96.43%)
Mutual labels:  state-machine
State Machine
🤖 A state machine library for Kotlin, with extensions for Android.
Stars: ✭ 72 (-94.53%)
Mutual labels:  state-machine
Kfin State Machine
Kotlin Finite State Machine
Stars: ✭ 36 (-97.26%)
Mutual labels:  state-machine
Pylstar
An implementation of the LSTAR Grammatical Inference Algorithm
Stars: ✭ 41 (-96.88%)
Mutual labels:  state-machine
River Admin
🚀 A shiny admin interface for django-river built with DRF, Vue & Vuetify
Stars: ✭ 55 (-95.82%)
Mutual labels:  state-machine
Oqaml
An OCaml based implementation of a Quil QVM
Stars: ✭ 31 (-97.64%)
Mutual labels:  state-machine
Tulip Control
Temporal Logic Planning toolbox
Stars: ✭ 81 (-93.84%)
Mutual labels:  state-machine
Workflow
A Swift and Kotlin library for making composable state machines, and UIs driven by those state machines.
Stars: ✭ 860 (-34.65%)
Mutual labels:  state-machine
React Context Hook
A React.js global state manager with Hooks
Stars: ✭ 50 (-96.2%)
Mutual labels:  state-machine
Finity
A finite state machine library for Node.js and the browser with a friendly configuration DSL.
Stars: ✭ 88 (-93.31%)
Mutual labels:  state-machine
Statemachineone
State Machine library for PHP
Stars: ✭ 84 (-93.62%)
Mutual labels:  state-machine
Hal
🔴 A non-deterministic finite-state machine for Android & JVM that won't let you down
Stars: ✭ 63 (-95.21%)
Mutual labels:  state-machine

npm Build Status tested with jest code style: prettier

React Automata

A state machine abstraction for React that provides declarative state management and automatic test generation.

Quick Start

Installation

react and react-test-renderer are peer dependencies.

yarn add react-automata

Usage

// App.js

import React from 'react'
import { Action, withStateMachine } from 'react-automata'

const statechart = {
  initial: 'a',
  states: {
    a: {
      on: {
        NEXT: 'b',
      },
      onEntry: 'sayHello',
    },
    b: {
      on: {
        NEXT: 'a',
      },
      onEntry: 'sayCiao',
    },
  },
}

class App extends React.Component {
  handleClick = () => {
    this.props.transition('NEXT')
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>NEXT</button>
        <Action is="sayHello">Hello, A</Action>
        <Action is="sayCiao">Ciao, B</Action>
      </div>
    )
  }
}

export default withStateMachine(statechart)(App)
// App.spec.js

import { testStateMachine } from 'react-automata'
import App from './App'

test('it works', () => {
  testStateMachine(App)
})
// App.spec.js.snap

exports[`it works: a 1`] = `
<div>
  <button
    onClick={[Function]}
  >
    NEXT
  </button>
  Hello, A
</div>
`;

exports[`it works: b 1`] = `
<div>
  <button
    onClick={[Function]}
  >
    NEXT
  </button>
  Ciao, B
</div>
`;

API

withStateMachine(statechart[, options])(Component)

The withStateMachine higher-order component accepts an xstate configuration object or an xstate machine, some options and a component. It returns a new component with special props, action and activity methods and additional lifecycle hooks. The initial machine state and the initial data can be passed to the resulting component through the initialMachineState and initialData props.

Options

Option Type Description
channel string The key of the context on which to set the state.
devTools bool To connect the state machine to the Redux DevTools Extension.

Props

transition(event[, updater])

The method to change the state of the state machine. It takes an optional updater function that receives the previous data and returns a data change. The updater can also be an object, which gets merged into the current data.

handleClick = () => {
  this.props.transition('FETCH')
}

machineState

The current state of the state machine.

It's not recommended to use this value because it couples the component and the state machine.

<button onClick={this.handleClick}>
  {this.props.machineState === 'idle' ? 'Fetch' : 'Retry'}
</button>

Action and Activity methods

All the component's methods whose names match the names of actions and activities, are fired when the related transition happen. Actions receive the state and the event as arguments. Activities receive a boolean that is true when the activity should start, and false otherwise.

For example:

const statechart = {
  // ...
  fetching: {
    on: {
      SUCCESS: 'success',
      ERROR: 'error',
    },
    onEntry: 'fetchGists',
  },
  // ...
}

class App extends React.Component {
  // ...
  fetchGists() {
    fetch('https://api.github.com/users/gaearon/gists')
      .then(response => response.json())
      .then(gists => this.props.transition('SUCCESS', { gists }))
      .catch(() => this.props.transition('ERROR'))
  }
  // ...
}

Lifecycle hooks

componentWillTransition(event)

The lifecycle method invoked when the transition function has been called. It provides the event, and can be used to run side-effects.

componentWillTransition(event) {
  if (event === 'FETCH') {
    fetch('https://api.github.com/users/gaearon/gists')
      .then(response => response.json())
      .then(gists => this.props.transition('SUCCESS', { gists }))
      .catch(() => this.props.transition('ERROR'))
  }
}

componentDidTransition(prevMachineState, event)

The lifecycle method invoked when a transition has happened and the state is updated. It provides the previous state machine, and the event. The current machineState is available in this.props.

componentDidTransition(prevMachineState, event) {
  Logger.log(event)
}

<Action />

The component to define which parts of the tree should be rendered for a given action (or set of actions).

Prop Type Description
is oneOfType(string, arrayOf(string)) The action(s) for which the children should be shown. It accepts the exact value, a glob expression or an array of values/expressions (e.g. is="fetch", is="show*" or is={['fetch', 'show*']).
channel string The key of the context from where to read the state.
children node The children to be rendered when the conditions match.
render func The render prop receives a bool (true when the conditions match) and it takes precedence over children.
onHide func The function invoked when the component becomes invisible.
onShow func The function invoked when the component becomes visible.
<Action is="showError">Oh, snap!</Action>
<Action
  is="showError"
  render={visible => (visible ? <div>Oh, snap!</div> : null)}
/>

<State />

The component to define which parts of the tree should be rendered for a given state (or set of states).

Prop Type Description
is oneOfType(string, arrayOf(string)) The state(s) for which the children should be shown. It accepts the exact value, a glob expression or an array of values/expressions (e.g. is="idle", is="error.*" or is={['idle', 'error.*']).
channel string The key of the context from where to read the state.
children node The children to be rendered when the conditions match.
render func The render prop receives a bool (true when the conditions match) and it takes precedence over children.
onHide func The function invoked when the component becomes invisible.
onShow func The function invoked when the component becomes visible.
<State is="error">Oh, snap!</State>
<State
  is="error"
  render={visible => (visible ? <div>Oh, snap!</div> : null)}
/>

testStateMachine(Component[, { fixtures, extendedState }])

The method to automagically generate tests given a component wrapped into withStateMachine. It accepts an additional fixtures option to describe the data to be injected into the component for a given transition, and an extendedState option to control the statechart's conditions - both are optional.

const fixtures = {
  initialData: {
    gists: [],
  },
  fetching: {
    SUCCESS: {
      gists: [
        {
          id: 'ID1',
          description: 'GIST1',
        },
        {
          id: 'ID2',
          description: 'GIST2',
        },
      ],
    },
  },
}

test('it works', () => {
  testStateMachine(App, { fixtures })
})

Examples

Frequently Asked Questions

You might find the answer to your question here.

Upgrades

The upgrade process is documented here.

Inspiration

Federico, for telling me "Hey, I think building UIs using state machines is the future".

David, for giving an awesome talk about infinitely better UIs, and building xstate.

Ryan, for experimenting with xstate and React - Ryan's approach to React has always been a source of inspiration to me.

Erik, for writing about statecharts, and showing me how to keep UI and state machine decoupled.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].