Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Created December 1, 2017 21:06
Show Gist options
  • Select an option

  • Save ryanflorence/91c949815b6ce7dbe5c37f1cd82e8d6e to your computer and use it in GitHub Desktop.

Select an option

Save ryanflorence/91c949815b6ce7dbe5c37f1cd82e8d6e to your computer and use it in GitHub Desktop.

Revisions

  1. ryanflorence created this gist Dec 1, 2017.
    112 changes: 112 additions & 0 deletions FiniteMachine.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,112 @@
    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 }