{"id":22602348,"url":"https://github.com/avaragado/xstateful-react","last_synced_at":"2025-04-11T02:42:30.843Z","repository":{"id":57099912,"uuid":"139357369","full_name":"avaragado/xstateful-react","owner":"avaragado","description":"Use xstateful with React, accessing states and activities from multiple statecharts anywhere in your app","archived":false,"fork":false,"pushed_at":"2018-08-08T11:25:55.000Z","size":331,"stargazers_count":30,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-25T00:34:52.239Z","etag":null,"topics":["context","finite-state-machine","harel","hierarchical-state-machine","interpreter","react","reducer","state-machine","statechart","xstate","xstateful"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/avaragado.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"code-of-conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-01T19:49:27.000Z","updated_at":"2024-02-08T19:41:37.000Z","dependencies_parsed_at":"2022-08-22T23:10:41.337Z","dependency_job_id":null,"html_url":"https://github.com/avaragado/xstateful-react","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avaragado%2Fxstateful-react","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avaragado%2Fxstateful-react/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avaragado%2Fxstateful-react/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avaragado%2Fxstateful-react/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/avaragado","download_url":"https://codeload.github.com/avaragado/xstateful-react/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248331182,"owners_count":21085854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["context","finite-state-machine","harel","hierarchical-state-machine","interpreter","react","reducer","state-machine","statechart","xstate","xstateful"],"created_at":"2024-12-08T12:20:14.637Z","updated_at":"2025-04-11T02:42:30.822Z","avatar_url":"https://github.com/avaragado.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @avaragado/xstateful-react\n\n\u003e Use [`@avaragado/xstateful`](https://github.com/avaragado/xstateful) with React, accessing states and activities from multiple statecharts anywhere in your app\n\nSee https://codesandbox.io/s/6lyq0yl4rz for a full example, simulating UK-style traffic lights and a pedestrian crossing (the source is in the `examples/pelican` directory).\n\n## Features\n\n-   **Provider/consumer model** Call a function to create React components from an `XStateful` instance. Add the provider near the app root, and use consumers lower down the tree to access machine state and extended state.\n-   **Specialised consumer components** Use declarative components that render based on machine activities, machine state, or any function.\n-   **Familiar props** Consumer components accept `component` prop, `render` prop, function-as-child, or child nodes, very similar to `react-router`.\n-   **Lifecycle helper** A special component lets you \"set up\" and \"tear down\" a machine on mount/unmount if you need to.\n\n### Requirements\n\n-   React 16.3+ (uses React's new \"context\" functionality)\n\n## Why?\n\nThe [`@avaragado/xstateful`](https://github.com/avaragado/xstateful) package is a self-contained interpreter for statecharts, wrapping `xstate` and adding support for reducers, extended state, and time-based events. `xstateful` itself doesn't render anything.\n\n`xstateful-react` works with `xstateful` to let you render React components based on your statechart, and trigger state transitions.\n\n## Terminology\n\nTerms are as used in `xstateful`, plus common terms in React development such as _render props_, _function-as-child_, and the provider/consumer pattern of React Context.\n\n## Installation\n\n```bash\n$ yarn add xstate @avaragado/xstateful @avaragado/xstateful-react\n$ # or\n$ npm install xstate @avaragado/xstateful @avaragado/xstateful-react\n```\n\n## Getting started\n\nIn summary:\n\n1.  Use `xstateful` to create an `XStateful` instance (either instantiating `XStateful` directly, or by calling `createStatefulMachine`).\n1.  Call the `xstateful-react` function `createReactMachine`, passing your `XStateful` instance. This function returns a number of components (a _provider_ and several _consumers_).\n1.  Add the provider component somewhere near the root of your app.\n1.  Add the consumer components as necessary as direct or indirect descendants of the provider.\n1.  Add the control component if needed to set up/tear down your machine at the right places in your app.\n\n### Example\n\nUse one module to export an `XStateful` instance and the React components generated by `xstateful-react`. You can test the `XStateful` instance in isolation, without worrying about particular rendering environments.\n\n```js\n// my-react-machine.js\n\nimport { Machine } from 'xstate';\nimport { createStatefulMachine } from '@avaragado/xstateful';\nimport { createReactMachine } from '@avaragado/xstateful-react';\n\nconst machine = Machine({\n    // xstate machine configuration\n});\nconst reducer = ... // if needed\nconst extstate = ... // if needed\n\nexport const xsf = createStatefulMachine({ machine, reducer, extstate });\n\nexport default createReactMachine(xsf);\n```\n\nNear the root of the render tree, add the provider component (it accepts no props). It doesn't have to look exactly like this: as long as the provider is an ancestor of all the consumers, it's fine.\n\nThe object returned by `createReactMachine` is a general-purpose consumer component, described in full below. It has properties for the other, more specialised consumers, plus a property for the provider, and one for the control component. This structure is intended to \"read well\" to aid understanding when inspecting a component tree.\n\n```js\n// my-root.jsx\n\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport MyApp from './my-app';\nimport MyReactMachine from './my-react-machine';\n\nReactDOM.render(\n    \u003cMyReactMachine.Provider\u003e\n        \u003cMyApp /\u003e\n    \u003c/MyReactMachine.Provider\u003e,\n    document.getElementById('root'),\n);\n```\n\nLower down the render tree, render the consumer components. They all accept `component`, `render` and `children` props that work almost identically to `react-router`. They also nest, with predictable results. Full descriptions of each component are below – this example component just shows a few options.\n\n```js\n// my-random-component.jsx\n\nimport React from 'react';\n\nimport MyReactMachine from './my-react-machine';\n\nconst MyRandomComponent = () =\u003e (\n    \u003cdiv\u003e\n        \u003cp\u003eBlah...\u003c/p\u003e\n\n        {/* the main component renders according to a function of state and extended state */}\n        \u003cMyReactMachine\n            cond={({ state, extstate }) =\u003e\n                state.value === 'boink' \u0026\u0026 extstate.foo === 'bar'\n            }\n        \u003e\n            \u003cp\u003eRendered if cond evaluates to true\u003c/p\u003e\n        \u003c/MyReactMachine\u003e\n\n        {/* the .Activity component renders according to current machine activities */}\n        \u003cMyReactMachine.Activity is=\"pending\"\u003e\n            Loading...\n        \u003c/MyReactMachine.Activity\u003e\n        \u003cMyReactMachine.Activity is={['error', 'timeout']}\u003e\n            Something went wrong\n        \u003c/MyReactMachine.Activity\u003e\n\n        {/* the .State component renders according to current machine state value */}\n        \u003cMyReactMachine.State is=\"a.b.c\"\u003e ... \u003c/MyReactMachine.State\u003e\n        \u003cMyReactMachine.State is={['a.b.c', 'a.b.d']}\u003e...\u003c/MyReactMachine.State\u003e\n    \u003c/div\u003e\n);\n\nexport default MyRandomComponent;\n```\n\nSome `XStateful` machines may need special \"set up\" and \"tear down\" behaviour. For example, consider a machine that sends an event on a periodic timer when it's in a particular state. This machine keeps sending that event even when it's not mounted in the component tree, for as long as it remains in this state. (This is because the `XStateful` instance is decoupled from React.)\n\nThis might be what's needed for an app: every app is different. Some apps might instead want to \"power down\" a machine when it's not mounted. Use the `Control` component to do this.\n\n```js\n// my-other-component.jsx\n\nimport React from 'react';\n\nimport MyReactMachine from './my-react-machine';\n\nconst MyOtherComponent = () =\u003e (\n    \u003cMyReactMachine.Control\n        onDidMount={({ transition }) =\u003e transition('POWER_ON');}\n        onWillUnmount={({ transition }) =\u003e transition('POWER_OFF');}\n    \u003e\n        ...other components, including consumer components from MyReactMachine\n    \u003c/MyReactMachine.Control\n);\n\nexport default MyOtherComponent;\n```\n\n## Reference\n\n### Module exports\n\n```js\nimport { createReactMachine } from '@avaragado/xstateful-react';\n```\n\n### `createReactMachine` function\n\n#### `createReactMachine(XStateful) =\u003e React.ComponentType`\n\nReturns a general-purpose React consumer component (let's call it `Machine`) tied to the input `XStateful` instance. `Machine` holds other components, as properties `Activity`, `State`, `Provider` and `Control`.\n\n### `Machine`\n\nThis is a React component that gives access to values from `Machine.Provider` rendered higher in the tree. Use these values, through props, to determine whether to render other components.\n\n`Machine` props:\n\n-   `cond?: boolean | (({ state, extstate }) =\u003e boolean)`\n-   `component?: React.ComponentType\u003c{ state, extstate, init, transition, setExtState }\u003e`\n-   `render?: ({ state, extstate, init, transition, setExtState }) =\u003e React.Node`\n-   `children?: React.Node | ({ state, extstate, init, transition, setExtState, match }) =\u003e React.Node`\n\nThe arguments/props `state`, `extstate`, `init`, `transition` and `setExtState` correspond to the `XStateful` instance: `state` contains machine state, `extstate` contains extended state, `init` is a function to initialise or reset the machine, `transition` is a function to send an event to the machine, and `setExtState` is a function to update extended state.\n\nIf more than one of `component`, `render` and `children` are specified, `component` takes precedence over `render`, and `render` takes precedence over `children`.\n\nThe boolean value from `cond` (which defaults to `true` if `cond` is omitted), combined with `component`/`render`/`children`, define what's rendered.\n\n-   With `component`:\n    -   When `cond` is true, creates a React element from that component, passing the props `state`, `extstate`, `init`, `transition` and `setExtState`, and renders that.\n    -   When `cond` is false, renders `null`.\n-   With `render`:\n    -   When `cond` is true, calls the render prop function passing a single object arg `{ state, extstate, init, transition, setExtState }` and renders the result.\n    -   When `cond` is false, renders `null`.\n-   With `children` nodes (not function-as-child form):\n    -   When `cond` is true, renders the children.\n    -   When `cond` is false, renders `null`.\n-   With `children` function:\n    -   Calls the function, passing a single object arg `{ state, extstate, init, transition, setExtState, match }`, where `match` is the boolean result of `cond`, and renders the result.\n\nExamples:\n\n```js\n\u003cMachine cond={other_value_in_scope}\u003e\n    \u003cp\u003eRendered only if cond value is true\u003c/p\u003e\n\u003c/Machine\u003e\n\n\u003cMachine cond={mc =\u003e mc.extstate.foo === 123}\u003e\n    \u003cp\u003eRendered only if cond evaluates to true\u003c/p\u003e\n\u003c/Machine\u003e\n\n\u003cMachine cond={mc =\u003e mc.extstate.foo === 123}\u003e\n    {({ match }) =\u003e (\n        \u003cp\u003eRendered always, cond result is in match\u003c/p\u003e\n    )}\n\u003c/Machine\u003e\n\n\u003cMachine\n    cond={mc =\u003e mc.state.actions.length === 0}\n    component={RenderedOnlyIfCondTrue}\n/\u003e\n\n\u003cMachine\n    cond={mc =\u003e otherfunction(mc, othervalue)}\n    render={\n        ({ state }) =\u003e (\u003cp\u003erendered only if cond true\u003c/p\u003e)\n    }\n/\u003e\n\n\u003cMachine\u003e\n    {({ state, extstate, init, transition, setExtState }) =\u003e {\n        // render something!\n    }}\n\u003c/Machine\u003e\n```\n\n### `Machine.Activity`\n\nThis is a React component that sprinkles some sugar over `Machine`, focusing on the activities emitted by the statechart.\n\n`Machine.Activity` props:\n\n-   `is?: string | Array\u003cstring\u003e | (({ [activity: string]: boolean }) =\u003e boolean)`\n-   `not?: string | Array\u003cstring\u003e | (({ [activity: string]: boolean }) =\u003e boolean)`\n-   `component?: React.ComponentType\u003c{ state, extstate, init, transition, setExtState }\u003e`\n-   `render?: ({ state, extstate, init, transition, setExtState }) =\u003e React.Node`\n-   `children?: React.Node | ({ state, extstate, init, transition, setExtState, match }) =\u003e React.Node`\n\nThe `is` and `not` props check against the statechart's current activities.\n\n-   `is=\"foo\"` matches if the string is a current activity.\n-   `not=\"foo\"` matches if the string is not a current activity.\n-   `is={['foo', 'bar']}` matches if any of the array members is a current activity.\n-   `not={['foo', 'bar']}` matches if none of the array members is a current activity.\n-   `is={myFunction}` matches if the function, when passed an object `{ [activity: string]: boolean }` describing the statechart's current activities, returns true.\n-   `not={myFunction}` matches if the function, when passed an object `{ [activity: string]: boolean }` describing the statechart's current activities, returns false.\n\nThe boolean result of the match feeds in to the `component`, `render` and `children` props as described above for `Machine`.\n\nExamples:\n\n```js\n\u003cMachine.Activity is=\"buzzing\"\u003e\n    \u003cp\u003ePower is on!\u003c/p\u003e\n\u003c/Machine.Activity\u003e\n\n\u003cMachine.Activity\n    not={['fizzing', 'buzzing']}\n    component={MostlyHarmless}\n/\u003e\n\n\u003cMachine.Activity is=\"open\"\u003e\n    {({ match }) =\u003e (\n        \u003cp\u003ematch is true if activity \"open\" is current, false otherwise\u003c/p\u003e\n    )}\n\u003c/Machine.Activity\u003e\n```\n\n### `Machine.State`\n\nThis is a React component that sprinkles some sugar over `Machine`, focusing on the current state(s) of the statechart.\n\n`Machine.State` props:\n\n-   `is?: string | Array\u003cstring\u003e | (({ [activity: string]: boolean }) =\u003e boolean)`\n-   `not?: string | Array\u003cstring\u003e | (({ [activity: string]: boolean }) =\u003e boolean)`\n-   `component?: React.ComponentType\u003c{ state, extstate, init, transition, setExtState }\u003e`\n-   `render?: ({ state, extstate, init, transition, setExtState }) =\u003e React.Node`\n-   `children?: React.Node | ({ state, extstate, init, transition, setExtState, match }) =\u003e React.Node`\n\nThe `is` and `not` props check against the statechart's current states.\n\n-   `is=\"foo\"` matches if the string is a current state.\n-   `not=\"foo\"` matches if the string is not a current state.\n-   `is={['foo', 'bar']}` matches if any of the array members is a current state.\n-   `not={['foo', 'bar']}` matches if none of the array members is a current state.\n-   `is={myFunction}` matches if the function, when passed an `xstate` state value, returns true.\n-   `not={myFunction}` matches if the function, when passed an `xstate` state value, returns false.\n\nThe state check uses the `xstate` utility `matchesState`, and supports parallel and nested states. For example, if the statechart is currently in states `a.b.c` and `a.b.d`, then a check `is=\"a.b\"` will match.\n\nThe boolean result of the match feeds in to the `component`, `render` and `children` props as described above for `Machine`.\n\nExamples:\n\n```js\n\u003cMachine.State is=\"idle\"\u003e\n    \u003cp\u003eWaiting for input\u003c/p\u003e\n\u003c/Machine.State\u003e\n\n\u003cMachine.State\n    not={['a.b', 'a.c']}\n    render={({ exstate }) =\u003e (\n        \u003cSomething val={extstate.foo} /\u003e\n    )}\n/\u003e\n\n\u003cMachine.State is={someComplexFunctionOfStateValue}\u003e\n    {({ match }) =\u003e (\n        \u003cp\u003ematch is boolean result of function\u003c/p\u003e\n    )}\n\u003c/Machine.State\u003e\n```\n\n### `Machine.Provider`\n\nThis React component holds the link to the `XStateful` instance for the statechart. It provides current machine state and extended state values, and functions to send events, to all related consumer components (`Machine`, `Machine.Activity`, `Machine.State`) rendered as descendants in the render tree.\n\nNo props.\n\n### `Machine.Control`\n\nThis React component includes two props that map to React lifecycle methods. Use these props to initialise and/or send events to the statechart when the React component mounts and/or unmounts. Not all apps need to use it.\n\n`Machine.Control` props:\n\n-   `onDidMount?: ({ state, extstate, init, transition, setExtState }) =\u003e void`\n-   `onWillUnmount?: ({ state, extstate, init, transition, setExtState }) =\u003e void`\n-   `children: React.Node`\n\nThe arguments/props `state`, `extstate`, `init`, `transition` and `setExtState` correspond to the `XStateful` instance: `state` contains machine state, `extstate` contains extended state, `init` is a function to initialise or reset the machine, `transition` is a function to send an event to the machine, and `setExtState` is a function to update extended state.\n\nThe component always renders its children.\n\nThe component calls the `onDidMount` function in its own `componentDidMount` lifecycle method, and `onWillUnmount` in its own `componentWillUnmount` lifecycle method.\n\nExamples:\n\n```js\n\u003cMachine.Control onDidMount={({ init }) =\u003e init()}\u003e\n    ...\n\u003c/Machine.Control\u003e\n\n\u003cMachine.Control\n    onDidMount={({ transition }) =\u003e transition('POWER_ON')}\n    onWillUnmount={({ transition }) =\u003e transition('POWER_OFF')}\n\u003e\n    ...\n\u003c/Machine.Control\u003e\n```\n\n## Meta\n\n### Inspiration\n\n-   [`xstate`](https://github.com/davidkpiano/xstate), by David Khourshid\n-   [`react-finite-machine`](https://github.com/derek-duncan/react-finite-machine), by Derek Duncan\n-   [`react-automata`](https://github.com/MicheleBertoli/react-automata), by Michele Bertoli\n\n### Maintainer\n\nDavid Smith (@avaragado)\n\n### Contribute\n\nBug reports, feature requests and PRs are gratefully received. [Add an issue](https://github.com/avaragado/xstateful-react/issues/new) or submit a PR.\n\nPlease note that this project is released with a [Contributor Code of Conduct](code-of-conduct.md). By participating in this project you agree to abide by its terms.\n\n### Contributors\n\nThanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore --\u003e\n| [\u003cimg src=\"https://avatars2.githubusercontent.com/u/886041?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDavid Smith\u003c/b\u003e\u003c/sub\u003e](http://avaragado.org)\u003cbr /\u003e[📖](https://github.com/avaragado/xstateful-react/commits?author=avaragado \"Documentation\") [💻](https://github.com/avaragado/xstateful-react/commits?author=avaragado \"Code\") [⚠️](https://github.com/avaragado/xstateful-react/commits?author=avaragado \"Tests\") | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/4660659?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eShMcK\u003c/b\u003e\u003c/sub\u003e](http://medium.com/@shmck)\u003cbr /\u003e[📖](https://github.com/avaragado/xstateful-react/commits?author=ShMcK \"Documentation\") |\n| :---: | :---: |\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!\n\n### Developer notes\n\nThe `package.json` file contains all the usual scripts for linting, testing, building and releasing.\n\nBuzzwords: prettier, eslint, flow, flow-typed, babel, jest, rollup, react.\n\n#### Branches and merging\n\nWhen merging to master **Squash and Merge**.\n\nIn the commit message, follow [conventional-changelog-standard conventions](https://github.com/bcoe/conventional-changelog-standard/blob/master/convention.md)\n\n#### Releasing\n\nWhen ready to release to npm:\n\n1.  `git checkout master`\n1.  `git pull origin master`\n1.  `yarn release:dryrun`\n1.  `yarn release`\n1.  Engage pre-publication paranoia\n1.  `git push --follow-tags origin master`\n1.  `npm publish` - not yarn here as yarn doesn't seem to respect publishConfig\n\n### Licence\n\n[MIT](LICENSE.txt) © David Smith\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favaragado%2Fxstateful-react","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Favaragado%2Fxstateful-react","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favaragado%2Fxstateful-react/lists"}