@xstate/vue

The @xstate/vue package contains utilities for using XState with Vue.

[[toc]]

::: warning Vue 2 Notice: If you’re using Vue 2.x, please see the Vue recipe instead, or use the xstate-vue2 package if you want to use the Vue Composition API. :::

Quick Start

  1. Install xstate and @xstate/vue:
  1. npm i xstate @xstate/vue

Via CDN

  1. <script src="https://unpkg.com/@xstate/vue/dist/xstate-vue.min.js"></script>

By using the global variable XStateVue

or

  1. <script src="https://unpkg.com/@xstate/vue/dist/xstate-vue.fsm.min.js"></script>

By using the global variable XStateVueFSM

  1. Import the useMachine composition function:
  1. <template>
  2. <button @click="send('TOGGLE')">
  3. {{
  4. state.value === 'inactive'
  5. ? 'Click to activate'
  6. : 'Active! Click to deactivate'
  7. }}
  8. </button>
  9. </template>
  10. <script>
  11. import { useMachine } from '@xstate/vue';
  12. import { createMachine } from 'xstate';
  13. const toggleMachine = createMachine({
  14. id: 'toggle',
  15. initial: 'inactive',
  16. states: {
  17. inactive: {
  18. on: { TOGGLE: 'active' }
  19. },
  20. active: {
  21. on: { TOGGLE: 'inactive' }
  22. }
  23. }
  24. });
  25. export default {
  26. setup() {
  27. const { state, send } = useMachine(toggleMachine);
  28. return {
  29. state,
  30. send
  31. };
  32. }
  33. };
  34. </script>

API

useMachine(machine, options?)

A Vue composition function that interprets the given machine and starts a service that runs for the lifetime of the component.

Arguments

  • machine - An XState machine.
  • options (optional) - Interpreter options OR one of the following Machine Config options: guards, actions, activities, services, delays, immediate, context, or state.

Returns { state, send, service}:

  • state - Represents the current state of the machine as an XState State object.
  • send - A function that sends events to the running service.
  • service - The created service.

useService(service)

A Vue composition function that subscribes to state changes from an existing service.

Arguments

Returns {state, send}:

  • state - Represents the current state of the service as an XState State object.
  • send - A function that sends events to the running service.

useActor(actor, getSnapshot)

A Vue composition function that subscribes to emitted changes from an existing actor.

Since 0.5.0

Arguments

  • actor - an actor-like object that contains .send(...) and .subscribe(...) methods.
  • getSnapshot - a function that should return the latest emitted value from the actor.
    • Defaults to attempting to get the actor.state, or returning undefined if that does not exist.
  1. import { useActor } from '@xstate/vue';
  2. export default {
  3. props: ['someSpawnedActor'],
  4. setup(props) {
  5. const { state, send } = useActor(props.someSpawnedActor);
  6. return { state, send };
  7. }
  8. };

useInterpret(machine, options?, observer?)

A Vue composition function that returns the service created from the machine with the options, if specified. It also sets up a subscription to the service with the observer, if provided.

Since 0.5.0

Arguments

  • machine - An XState machine or a function that lazily returns a machine.
  • options (optional) - Interpreter options and/or any of the following machine config options: guards, actions, services, delays, immediate, context, state.
  • observer (optional) - an observer or listener that listens to state updates:
    • an observer (e.g., { next: (state) => {/* ... */} })
    • or a listener (e.g., (state) => {/* ... */})
  1. import { useInterpret } from '@xstate/vue';
  2. import { someMachine } from '../path/to/someMachine';
  3. export default {
  4. setup() {
  5. const service = useInterpret(someMachine);
  6. return service;
  7. }
  8. };

With options + listener:

  1. import { useInterpret } from '@xstate/vue';
  2. import { someMachine } from '../path/to/someMachine';
  3. export default {
  4. setup() {
  5. const service = useInterpret(
  6. someMachine,
  7. {
  8. actions: {
  9. /* ... */
  10. }
  11. },
  12. (state) => {
  13. // subscribes to state changes
  14. console.log(state.value);
  15. }
  16. );
  17. // ...
  18. }
  19. };

useSelector(actor, selector, compare?, getSnapshot?)

A Vue composition function that returns the selected value from the snapshot of an actor, such as a service. This hook will only cause a rerender if the selected value changes, as determined by the optional compare function.

Since 0.6.0

Arguments

  • actor - a service or an actor-like object that contains .send(...) and .subscribe(...) methods.
  • selector - a function that takes in an actor’s “current state” (snapshot) as an argument and returns the desired selected value.
  • compare (optional) - a function that determines if the current selected value is the same as the previous selected value.
  • getSnapshot (optional) - a function that should return the latest emitted value from the actor.
    • Defaults to attempting to get the actor.state, or returning undefined if that does not exist. Will automatically pull the state from services.
  1. import { useSelector } from '@xstate/vue';
  2. const selectCount = (state) => state.context.count;
  3. export default {
  4. props: ['service'],
  5. setup(props) {
  6. const count = useSelector(props.service, selectCount);
  7. // ...
  8. return { count };
  9. }
  10. };

With compare function:

  1. import { useSelector } from '@xstate/vue';
  2. const selectUser = (state) => state.context.user;
  3. const compareUser = (prevUser, nextUser) => prevUser.id === nextUser.id;
  4. export default {
  5. props: ['service'],
  6. setup(props) {
  7. const user = useSelector(props.service, selectUser, compareUser);
  8. // ...
  9. return { user };
  10. }
  11. };

With useInterpret(...):

  1. import { useInterpret, useSelector } from '@xstate/vue';
  2. import { someMachine } from '../path/to/someMachine';
  3. const selectCount = (state) => state.context.count;
  4. export default {
  5. setup() {
  6. const service = useInterpret(someMachine);
  7. const count = useSelector(service, selectCount);
  8. // ...
  9. return { count, service };
  10. }
  11. };

useMachine(machine) with @xstate/fsm

A Vue composition function that interprets the given finite state machine from [@xstate/fsm] and starts a service that runs for the lifetime of the component.

This special useMachine hook is imported from @xstate/vue/lib/fsm

Arguments

Returns an object {state, send, service}:

  • state - Represents the current state of the machine as an @xstate/fsm StateMachine.State object.
  • send - A function that sends events to the running service.
  • service - The created @xstate/fsm service.

Example (TODO)

Configuring Machines

Existing machines can be configured by passing the machine options as the 2nd argument of useMachine(machine, options).

Example: the 'fetchData' service and 'notifySuccess' action are both configurable:

  1. <template>
  2. <template v-if="state.value === 'idle'">
  3. <button @click="send('FETCH', { query: 'something' })">
  4. Search for something
  5. </button>
  6. </template>
  7. <template v-else-if="state.value === 'loading'">
  8. <div>Searching...</div>
  9. </template>
  10. <template v-else-if="state.value === 'success'">
  11. <div>Success! {{ state.context.data }}</div>
  12. </template>
  13. <template v-else-if="state.value === 'failure'">
  14. <p>{{ state.context.error.message }}</p>
  15. <button @click="send('RETRY')">Retry</button>
  16. </template>
  17. </template>
  18. <script>
  19. import { assign, createMachine } from 'xstate';
  20. import { useMachine } from '@xstate/vue';
  21. const fetchMachine = createMachine({
  22. id: 'fetch',
  23. initial: 'idle',
  24. context: {
  25. data: undefined,
  26. error: undefined
  27. },
  28. states: {
  29. idle: {
  30. on: { FETCH: 'loading' }
  31. },
  32. loading: {
  33. invoke: {
  34. src: 'fetchData',
  35. onDone: {
  36. target: 'success',
  37. actions: assign({
  38. data: (_context, event) => event.data
  39. })
  40. },
  41. onError: {
  42. target: 'failure',
  43. actions: assign({
  44. error: (_context, event) => event.data
  45. })
  46. }
  47. }
  48. },
  49. success: {
  50. entry: 'notifySuccess',
  51. type: 'final'
  52. },
  53. failure: {
  54. on: {
  55. RETRY: 'loading'
  56. }
  57. }
  58. }
  59. });
  60. export default {
  61. props: {
  62. onResolve: {
  63. type: Function,
  64. default: () => {}
  65. }
  66. },
  67. setup(props) {
  68. const { state, send } = useMachine(fetchMachine, {
  69. actions: {
  70. notifySuccess: (ctx) => props.onResolve(ctx.data)
  71. },
  72. services: {
  73. fetchData: (_context, event) =>
  74. fetch(`some/api/${event.query}`).then((res) => res.json())
  75. }
  76. });
  77. return {
  78. state,
  79. send
  80. };
  81. }
  82. };
  83. </script>

Matching States

For hierarchical and parallel machines, the state values will be objects, not strings. In this case, it’s better to use state.matches(...):

  1. <template>
  2. <div>
  3. <loader-idle v-if="state.matches('idle')" />
  4. <loader-loading-user v-if-else="state.matches({ loading: 'user' })" />
  5. <loader-loading-friends v-if-else="state.matches({ loading: 'friends' })" />
  6. </div>
  7. </template>

Persisted and Rehydrated State

You can persist and rehydrate state with useMachine(...) via options.state:

  1. <script>
  2. // Get the persisted state config object from somewhere, e.g. localStorage
  3. const persistedState = JSON.parse(
  4. localStorage.getItem('some-persisted-state-key')
  5. );
  6. export default {
  7. setup() {
  8. const { state, send } = useMachine(someMachine, {
  9. state: persistedState
  10. });
  11. // state will initially be that persisted state, not the machine's initialState
  12. return { state, send };
  13. }
  14. };
  15. </script>

Migration from 0.4.0

  • For spawned actors created using invoke or spawn(...), use the useActor() hook instead of useService():

    1. -import { useService } from '@xstate/vue';
    2. +import { useActor } from '@xstate/vue';
    3. -const {state, send} = useService(someActor);
    4. +const {state, send} = useActor(someActor);