Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Created December 1, 2017 21:06
Show Gist options
  • Save ryanflorence/91c949815b6ce7dbe5c37f1cd82e8d6e to your computer and use it in GitHub Desktop.
Save ryanflorence/91c949815b6ce7dbe5c37f1cd82e8d6e to your computer and use it in GitHub Desktop.
finite-machine.js
import React, { Component } from "react"
import { Machine } from "xstate"
import * as PropTypes from "prop-types"
class FiniteMachine extends Component {
machine = Machine(this.props.chart)
state = {
data: this.props.reducer(undefined, { type: "@init" }),
machineState: this.machine.getInitialState()
}
transition = (actionType, newData) => {
const { log, chart, reducer } = this.props
const { data, machineState } = this.state
const nextState = this.machine.transition(machineState, actionType).toString()
const action = {
data: newData,
nextState,
type: `${machineState}.${actionType}`
}
if (log) {
console.log(chart.id, action.type, action.data)
}
this.setState(
{
data: reducer(data, action),
machineState: nextState
},
log
? () => {
console.log(chart.id, this.state.machineState, this.state.data)
}
: undefined
)
}
render() {
return this.props.render({
state: this.state.machineState,
data: this.state.data,
transition: this.transition
})
}
}
class Match extends Component {
static propTypes = {
machine: PropTypes.shape({
state: PropTypes.string,
transition: PropTypes.func
}),
state: PropTypes.string,
partial: PropTypes.bool,
conditional: PropTypes.bool
}
static defaultProps = {
partial: false,
conditional: true
}
render() {
const { component: Component, render, partial, conditional, machine, state } = this.props
const match = partial ? machine.state.startsWith(state) : machine.state === state
return conditional ? (
match ? (
render ? (
render(machine)
) : (
<Component {...machine} />
)
) : null
) : render ? (
render(machine)
) : (
<Component {...machine} />
)
}
}
class Switch extends Component {
static propTypes = {
machine: PropTypes.shape({
state: PropTypes.string.isRequired,
transition: PropTypes.func.isRequired
}).isRequired
}
render() {
const { children, machine } = this.props
let match = null
React.Children.forEach(children, child => {
if (match) return
if (child.props.state === machine.state) {
match = child
}
})
return React.cloneElement(match, { machine })
}
}
export { FiniteMachine, Match, Switch }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment