Skip to content

Instantly share code, notes, and snippets.

@UberMouse
Created January 29, 2020 23:05
Show Gist options
  • Save UberMouse/025c2dece58b2566e512a225d167ad2e to your computer and use it in GitHub Desktop.
Save UberMouse/025c2dece58b2566e512a225d167ad2e to your computer and use it in GitHub Desktop.
import { StateMachine, StateSchema, EventObject, Typestate, Interpreter, State } from "xstate";
import * as _ from "lodash";
import React, { useContext } from "react";
export function makeMachineRoutable<
TContext,
TStateSchema extends StateSchema,
TEvent extends EventObject,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
TTypestate extends Typestate<TContext> = any
>(_machine: StateMachine<TContext, TStateSchema, TEvent, TTypestate>) {
type Send = Interpreter<TContext, TStateSchema, TEvent, TTypestate>["send"];
type Value = [Send, State<TContext, TEvent, TTypestate>];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const MachineContext = React.createContext<Value>(null as any);
const useMachine = () => useContext(MachineContext);
type Props<T extends TTypestate["value"]> = {
state: T | T[];
children: (args: {
context: Extract<TTypestate, { value: T | T[] }>["context"];
send: Send;
}) => JSX.Element;
};
function Match<T extends TTypestate["value"] = TTypestate["value"]>({
state,
children
}: Props<T>) {
const [send, current] = useMachine();
const matched = _.isArray(state)
? _.some(state, s => current.matches(s))
: current.matches(state);
if (matched) {
// Not sure what's going on here. Doesn't really matter, I don't think this can generate useful type safety
// What matters is that the child functions get passed the correct context based on the matched state
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return children({ context: current.context as any, send });
}
return null;
}
return {
provider: MachineContext.Provider,
useMachine,
Match
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment