{"id":13450269,"url":"https://github.com/rkrupinski/use-state-machine","last_synced_at":"2025-04-30T05:31:56.648Z","repository":{"id":57138996,"uuid":"426725511","full_name":"rkrupinski/use-state-machine","owner":"rkrupinski","description":"A simple yet powerful finite state machine React hook.","archived":false,"fork":false,"pushed_at":"2023-01-19T14:28:41.000Z","size":206,"stargazers_count":17,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-15T13:52:03.998Z","etag":null,"topics":["hook","react","state-machine","state-management","typescript"],"latest_commit_sha":null,"homepage":"https://github.com/rkrupinski/use-state-machine#readme","language":"TypeScript","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/rkrupinski.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-11-10T18:04:55.000Z","updated_at":"2023-01-19T08:14:22.000Z","dependencies_parsed_at":"2023-02-11T04:15:47.111Z","dependency_job_id":null,"html_url":"https://github.com/rkrupinski/use-state-machine","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkrupinski%2Fuse-state-machine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkrupinski%2Fuse-state-machine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkrupinski%2Fuse-state-machine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkrupinski%2Fuse-state-machine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rkrupinski","download_url":"https://codeload.github.com/rkrupinski/use-state-machine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251649038,"owners_count":21621466,"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":["hook","react","state-machine","state-management","typescript"],"created_at":"2024-07-31T07:00:33.090Z","updated_at":"2025-04-30T05:31:56.372Z","avatar_url":"https://github.com/rkrupinski.png","language":"TypeScript","funding_links":[],"categories":["Packages"],"sub_categories":[],"readme":"# @rkrupinski/use-state-machine\n\nA simple yet powerful finite state machine [React](https://reactjs.org/) hook.\n\n![Build status](https://github.com/rkrupinski/use-state-machine/workflows/CI/badge.svg)\n[![minified + gzip](https://badgen.net/bundlephobia/minzip/@rkrupinski/use-state-machine)](https://bundlephobia.com/package/@rkrupinski/use-state-machine)\n\n```ts\nconst [state, send] = useStateMachine({\n  initial: \"enabled\",\n  states: {\n    enabled: {\n      on: { TOGGLE: \"disabled\" },\n    },\n    disabled: {\n      on: { TOGGLE: \"enabled\" },\n    },\n  },\n});\n```\n\n\u003cbr /\u003e\n\nComes packed with features like:\n\n- effects (state entry/exit)\n- guards (allow/prevent state transitions)\n- extended state (context)\n- good to very good TypeScript experience (see [History](#history))\n\n\u003cbr /\u003e\n\nTable of contents:\n\n- [History](#history)\n- [Installation](#installation)\n- [Examples](#examples)\n- [API](#api)\n  - [State](#state)\n  - [Events](#events)\n  - [Machine options](#machine-options)\n  - [Configuring states](#configuring-states)\n  - [Effects](#effects)\n  - [Configuring state transitions](#configuring-state-transitions)\n  - [Guards](#guards)\n- [Event payload](#event-payload)\n- [Context](#context)\n- [Further reading](#further-reading)\n\n## History\n\nThis project was born as an attempt to reimplement [@cassiozen/usestatemachine](https://github.com/cassiozen/useStateMachine) in a more \"friendly\" way. Despite only weighing \u003c1kB, I found the reference project being slightly overly complex, especially on the type system side of things.\n\nℹ️ Note: This is based on version [1.0.0-beta.4](https://github.com/cassiozen/useStateMachine/releases/tag/1.0.0-beta.4) ([source code](https://github.com/cassiozen/useStateMachine/tree/ced39beb8a119a1acb264d62f522cfa419f9e85b))\n\nDifferences compared to the reference project:\n\n- simpler implementation\n- simpler types (with added benefit of making invalid/orphan states impossible)\n- manual payload typing/decoding (in place of \"[schema](https://github.com/cassiozen/useStateMachine/tree/ced39beb8a119a1acb264d62f522cfa419f9e85b#schema-context--event-typing)\"; see [Event payload](#event-payload) for details)\n- manual context typing (in place of \"[schema](https://github.com/cassiozen/useStateMachine/tree/ced39beb8a119a1acb264d62f522cfa419f9e85b#schema-context--event-typing)\"; see [Context](#context) for details)\n\n## Installation\n\n```\nnpm install @rkrupinski/use-state-machine\n```\n\n## Examples\n\nView [source code](packages/examples) or [live](https://use-state-machine.netlify.app).\n\nExamples cover:\n\n- a basic machine with context and guards\n- sending events with payload\n- http with error recovery\n\n## API\n\n### State\n\n```ts\nconst [\n  state, // \u003c--- this guy\n  send,\n] = useStateMachine(/* ... */);\n```\n\n`state` is an object of the following shape:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003evalue\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003estring\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      The name of the current state.\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003enextEvents\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003estring[]\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      The names of possible events.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#events\"\u003eEvents\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eevent\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eEvent\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      The event that led to the current state.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#events\"\u003eEvents\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003econtext\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eC\u003c/code\u003e (inferred)\n    \u003c/td\u003e\n    \u003ctd\u003e\n      Machine's extended state. Think of it as a place to store additional, machine-related data throughout its whole lifecycle.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#context\"\u003eContext\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### Events\n\n```ts\nconst [\n  state,\n  send, // \u003c--- this guy\n] = useStateMachine(/* ... */);\n```\n\nOnce initialized, events can be sent to the machine using the `send` function.\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003esend\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003e(event: string | Event) =\u003e void\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      Sends events to the machine\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nWhen sending events you can either use a shorthand (`string`) syntax:\n\n```ts\nsend(\"START\");\n```\n\nor the object (`Event`) syntax:\n\n```ts\nsend({ type: \"START\" });\n```\n\nUnder the hood, all sent events are normalized to objects (`Event`).\n\nℹ️ Note: The reason behind having 2 formats is that events, apart from being of certain `type`, can also carry `payload`.\n\n(see [Event payload](#event-payload))\n\n### Machine options\n\n```ts\nconst [state, send] = useStateMachine({\n  initial: \"idle\",\n  states: {\n    /* ... */\n  },\n  context: 42,\n});\n```\n\nMachine can be configured with the following options:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003einitial\u003c/code\u003e (required)\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003estring\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      The initial machine state value.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      ℹ️ Note: Must be a key of \u003ccode\u003estates\u003c/code\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003estates\u003c/code\u003e (required)\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003e{ [key: string]: StateConfig }\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      An object with configuration for all the states.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#configuring-states\"\u003eConfiguring states\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003econtext\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eC\u003c/code\u003e (inferred)\n    \u003c/td\u003e\n    \u003ctd\u003e\n      Initial context value.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#context\"\u003eContext\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### Configuring states\n\nYou can configure individual states using the `states` field of the machine options.\n\n```ts\nconst [state, send] = useStateMachine({\n  /* ... */\n  states: {\n    idle: {\n      on: {\n        START: \"running\",\n      },\n      effect() {\n        console.log(\"idling\");\n      },\n    },\n    /* ... */\n  },\n});\n```\n\nKeys of the `states` object are state names, values are `StateConfig` object of the following shape:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eon\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003e{ [key: string]: string | EvtConfig }\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      An object with configuration for all the transitions supported by this particular state.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#configuring-state-transitions\"\u003eConfiguring state transitions\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eeffect\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eEffect\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      A callback fired once the machine has transitioned to a particular state.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#effects\"\u003eEffects\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nℹ️ Note: There can't be a state that's neither initial, nor can be transitioned to.\n\n### Effects\n\nYou can define a callback to fire once the machine has transitioned to a particular state using the `effect` field.\n\n```ts\nconst [state, send] = useStateMachine({\n  /* ... */\n  states: {\n    idle: {\n      effect({ context, setContext, event, send }) {\n        console.log(\"idling due to\", event.type);\n\n        return () =\u003e {\n          console.log(\"idling no more\");\n        };\n      },\n    },\n    /* ... */\n  },\n});\n```\n\nThe `effect` callback will receive an object of the following shape:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003econtext\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eC\u003c/code\u003e (inferred)\n    \u003c/td\u003e\n    \u003ctd\u003e\n      The current value of the machine context.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#context\"\u003eContext\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003esetContext\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003e(updater: (context: C) =\u003e C) =\u003e void\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      A function to update the value of \u003ccode\u003econtext\u003c/code\u003e.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#context\"\u003eContext\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eevent\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eEvent\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      The event that triggered the current machine state.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#events\"\u003eEvents\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003esend\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003e(event: string | Event) =\u003e void\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      A function to send events to the machine.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#events\"\u003eEvents\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nIf the return value from `effect` is of type `function`, that function will be executed when the machine transitions away from the current state (exit/cleanup effect):\n\n```ts\neffect() {\n  console.log('entered a state');\n\n  return () =\u003e {\n    console.log('exited a state');\n  };\n},\n```\n\nℹ️ Note: Events are processed synchronously while effects are asynchronous. In other words, if several events are sent synchronously, e.g.:\n\n```ts\nsend(\"ONE\");\nsend(\"TWO\");\nsend(\"THREE\");\n```\n\nstate transitions will be performed accordingly, yet only the effect for state triggered by `THREE` (if defined) will be executed.\n\n### Configuring state transitions\n\nFor every state you can configure when and if a transition to a different state should be performed. This is done via the `on` property of `StateConfig`.\n\n```ts\nconst [state, send] = useStateMachine({\n  /* ... */\n  states: {\n    idle: {\n      on: {\n        START: \"running\",\n        FUEL_CHECK: {\n          target: \"off\",\n          guard() {\n            return isOutOfFuel();\n          },\n        },\n      },\n    },\n    off: {},\n  },\n});\n```\n\nTransition config can either be a `string` (denoting the target state value) or an object of the following shape:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003etarget\u003c/code\u003e (required)\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003estring\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      Target state value.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      ℹ️ Note: Must be a key of \u003ccode\u003estates\u003c/code\u003e.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#configuring-states\"\u003eConfiguring states\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eguard\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eGuard\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      A \u003ccode\u003eboolean\u003c/code\u003e-returning function to determine whether state transition is allowed.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#guards\"\u003eGuards\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### Guards\n\nThe purpose of guards is to determine whether state transition is allowed. A `guard` function is invoked before performing state transition and depending on its return value:\n\n- `true` ➡️ transition is performed\n- `false` ➡️ transition is prevented\n\nA `guard` function will receive an object of the following shape:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eevent\u003c/code\u003e \n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eEvent\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      The event that triggered state transition.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#events\"\u003eEvents\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ccode\u003econtext\u003c/code\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ccode\u003eC\u003c/code\u003e (inferred)\n    \u003c/td\u003e\n    \u003ctd\u003e\n      The current value of the machine context.\n      \u003cbr /\u003e\n      \u003cbr /\u003e\n      (see \u003ca href=\"#context\"\u003eContext\u003c/a\u003e)\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## Event payload\n\nWhen using the object (`Event`) syntax, you can send events with payload like so:\n\n```ts\nsend({\n  type: \"REFUEL\",\n  payload: { gallons: 5 },\n});\n```\n\nThe payload can be then consumed from:\n\n- the `state` object (see [State](#state))\n- `effect` functions (see [Effects](#effects))\n- `guard` functions (see [Guards](#guards))\n\nHow is it typed though? Is the type of `payload` inferred correctly?\n\nFor several reasons, the most important of which is simplicity (see [History](#history)), this library does neither aim at inferring, nor allows providing detailed event types. Instead, it encourages using other techniques, like:\n\n- Duck typing\n- Type guards\n- Decoders\n\nThe payload (`event.payload`) is always typed as `unknown` and it's up to the consumer to extract all the required information from it.\n\nHere's an example of a `guard` function that only allows refueling if the number of gallons is at least `5`, using [io-ts](https://github.com/gcanti/io-ts) to decode the `payload`:\n\n```ts\nimport * as t from \"io-ts\";\nimport { pipe } from 'fp-ts/function';\nimport { fold } from 'fp-ts/Either';\n\nconst RefuelPayload = t.type({\n  gallons: t.number,\n});\n\n/* ... */\n\nguard({ event }) {\n  const gallons = pipe(\n    RefuelPayload.decode(event.payload),\n    fold(\n      () =\u003e 0,\n      p =\u003e p.gallons,\n    ),\n  );\n\n  return gallons \u003e= 5;\n}\n```\n\n## Context\n\nAs mentioned above, the type of `context` is inferred from the initial value (see [Machine options](#machine-options)).\n\nType inference is straightforward for basic types like:\n\n- `42` ➡️ `number`\n- `'context'` ➡️ `string`\n- `[1, 2, 3]` ➡️ `number[]`\n\nIt gets tricky though if you need more complex constructs like:\n\n- type narrowing (`'foo'` vs `string`)\n- optionality (`{ foo?: string }`)\n- unions (`'foo' | 'bar'`)\n\nAgain, complex inference and annotating all the things through generic parameters is beyond the scope of this library (see [History](#history)). What it encourages instead is \"hinting\" TypeScript on the actual type of `context`.\n\nThis can be done via type assertions:\n\n```ts\ntype ContextType = \"foo\" | \"bar\";\n\nconst [state, send] = useStateMachine({\n  /* ... */\n  context: \"foo\" as ContextType,\n});\n\nstate.context; // 'foo' | 'bar'\n```\n\n## Further reading\n\n- [State machines](https://en.wikipedia.org/wiki/Finite-state_machine) on Wikipedia\n- [@cassiozen/usestatemachine](https://github.com/cassiozen/useStateMachine)\n- [XState](https://xstate.js.org/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frkrupinski%2Fuse-state-machine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frkrupinski%2Fuse-state-machine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frkrupinski%2Fuse-state-machine/lists"}