Skip to content

Commit

Permalink
feat(builders): viewToMachine
Browse files Browse the repository at this point in the history
Simple utility to reduce the boilerplate of bringing a bare React view into xstate-tree land

Closes View to machine utility #51
  • Loading branch information
UberMouse committed Jan 10, 2024
1 parent 0618f0c commit 0e86b5c
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 36 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.snap
*.typegen.ts
README.md
18 changes: 18 additions & 0 deletions src/builders.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { render, waitFor } from "@testing-library/react";
import React from "react";

import { viewToMachine } from "./builders";
import { buildRootComponent } from "./xstateTree";

describe("xstate-tree builders", () => {
describe("viewToMachine", () => {
it("takes a React view and wraps it in an xstate-tree machine that renders that view", async () => {
const ViewMachine = viewToMachine(() => <div>hello world</div>);
const Root = buildRootComponent(ViewMachine);

const { getByText } = render(<Root />);

await waitFor(() => getByText("hello world"));
});
});
});
35 changes: 27 additions & 8 deletions src/builders.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from "react";
import type {
EventObject,
StateMachine,
AnyStateMachine,
ContextFrom,
EventFrom,
InterpreterFrom,
AnyFunction,
import {
type EventObject,
type StateMachine,
type AnyStateMachine,
type ContextFrom,
type EventFrom,
type InterpreterFrom,
type AnyFunction,
createMachine,
} from "xstate";

import { Slot } from "./slots";
Expand All @@ -22,6 +23,7 @@ import {
XStateTreeMachineMetaV1,
XstateTreeMachineStateSchemaV1,
XstateTreeMachineStateSchemaV2,
AnyXstateTreeMachine,
} from "./types";

/**
Expand Down Expand Up @@ -261,3 +263,20 @@ export function createXStateTreeMachine<

return machine;
}

/**
* @public
*
* Simple utility builder to aid in integrating existing React views with xstate-tree
*
* @param view - the React view you want to invoke in an xstate machine
* @returns The view wrapped into an xstate-tree machine, ready to be invoked by other xstate machines or used with `buildRootComponent`
*/
export function viewToMachine(view: () => JSX.Element): AnyXstateTreeMachine {
return createXStateTreeMachine(
createMachine({ initial: "idle", states: { idle: {} } }),
{
View: view,
}
);
}
60 changes: 34 additions & 26 deletions src/test-app/AppMachine.typegen.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
// This file was automatically generated. Edits will be overwritten

export interface Typegen0 {
"@@xstate/typegen": true;
internalEvents: {
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
OtherMachine: "done.invoke.app.otherScreen:invocation[0]";
TodosMachine: "done.invoke.app.todos:invocation[0]";
};
missingImplementations: {
actions: never;
services: never;
guards: never;
delays: never;
};
eventsCausingActions: {};
eventsCausingServices: {
OtherMachine: "GO_SETTINGS";
TodosMachine: "GO_HOME";
};
eventsCausingGuards: {};
eventsCausingDelays: {};
matchesStates: "otherScreen" | "todos" | "waitingForRoute";
tags: never;
}
// This file was automatically generated. Edits will be overwritten

export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
"OtherMachine": "done.invoke.app.otherScreen:invocation[0]";
"TodosMachine": "done.invoke.app.todos:invocation[0]";
};
missingImplementations: {
actions: never;
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {

};
eventsCausingDelays: {

};
eventsCausingGuards: {

};
eventsCausingServices: {
"OtherMachine": "GO_SETTINGS";
"TodosMachine": "GO_HOME";
};
matchesStates: "otherScreen" | "todos" | "waitingForRoute";
tags: never;
}

1 change: 1 addition & 0 deletions src/xstateTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const getViewForInterpreter = memoize(
useEffect(() => {
if (activeRouteEvents) {
activeRouteEvents.forEach((event) => {
// @ts-ignore fixed in v5 branch
if (interpreter.state.nextEvents.includes(event.type)) {
interpreter.send(event);
}
Expand Down
7 changes: 5 additions & 2 deletions xstate-tree.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
```ts

import { AnyEventObject } from 'xstate';
import type { AnyFunction } from 'xstate';
import { AnyFunction } from 'xstate';
import { AnyStateMachine } from 'xstate';
import { BaseActionObject } from 'xstate';
import { ComponentPropsWithRef } from 'react';
import { ContextFrom } from 'xstate';
import { EventFrom } from 'xstate';
import { EventObject } from 'xstate';
import { History as History_2 } from 'history';
import type { InterpreterFrom } from 'xstate';
import { InterpreterFrom } from 'xstate';
import { JSXElementConstructor } from 'react';
import { ParsedQuery } from 'query-string';
import { default as React_2 } from 'react';
Expand Down Expand Up @@ -439,6 +439,9 @@ export type ViewProps<TSelectors, TActions, TSlots extends readonly Slot[], TMat
inState: TMatches;
};

// @public
export function viewToMachine(view: () => JSX.Element): AnyXstateTreeMachine;

// @public (undocumented)
export type XstateTreeHistory<T = unknown> = History_2<{
meta?: T;
Expand Down

0 comments on commit 0e86b5c

Please sign in to comment.