npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

xstate-vue2

v0.3.2

Published

XState tools for Vue

Downloads

7,169

Readme

xstate-vue2

Vue 2 composables for xstate and @xstate/fsm

Quick Start

  1. Install xstate (or @xstate/fsm) andxstate-vue2
npm i xstate xstate-vue2
  1. Import the useMachine composition function:
<template>
  <button @click="send('TOGGLE')">
    {{
      state.value === 'inactive'
        ? 'Click to activate'
        : 'Active! Click to deactivate'
    }}
  </button>
</template>

<script>
import { useMachine } from 'xstate-vue2';
import { createMachine } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: {
      on: { TOGGLE: 'active' }
    },
    active: {
      on: { TOGGLE: 'inactive' }
    }
  }
});

export default {
  setup() {
    const { state, send } = useMachine(toggleMachine);
    return {
      state,
      send
    };
  }
};
</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.
import { useActor } from 'xstate-vue2';

export default {
  props: ['someSpawnedActor'],
  setup(props) {
    const { state, send } = useActor(props.someSpawnedActor);
    return { state, send };
  }
};

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) => {/* ... */})
import { useInterpret } from 'xstate-vue2';
import { someMachine } from '../path/to/someMachine';
export default {
  setup() {
    const service = useInterpret(someMachine);
    return service;
  }
};

With options + listener:

import { useInterpret } from 'xstate-vue2';
import { someMachine } from '../path/to/someMachine';
export default {
  setup() {
    const service = useInterpret(
      someMachine,
      {
        actions: {
          /* ... */
        }
      },
      (state) => {
        // subscribes to state changes
        console.log(state.value);
      }
    );
    // ...
  }
};

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.
import { useSelector } from '@xstate/vue';
const selectCount = (state) => state.context.count;
export default {
  props: ['service'],
  setup(props) {
    const count = useSelector(props.service, selectCount);
    // ...
    return { count };
  }
};

With compare function:

import { useSelector } from '@xstate/vue';
const selectUser = (state) => state.context.user;
const compareUser = (prevUser, nextUser) => prevUser.id === nextUser.id;
export default {
  props: ['service'],
  setup(props) {
    const user = useSelector(props.service, selectUser, compareUser);
    // ...
    return { user };
  }
};

With useInterpret(...):

import { useInterpret, useSelector } from '@xstate/vue';
import { someMachine } from '../path/to/someMachine';
const selectCount = (state) => state.context.count;
export default {
  setup() {
    const service = useInterpret(someMachine);
    const count = useSelector(service, selectCount);
    // ...
    return { count, service };
  }
};

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.

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:

<template>
  <template v-if="state.value === 'idle'">
    <button @click="send('FETCH', { query: 'something' })">
      Search for something
    </button>
  </template>

  <template v-else-if="state.value === 'loading'">
    <div>Searching...</div>
  </template>

  <template v-else-if="state.value === 'success'">
    <div>Success! {{ state.context.data }}</div>
  </template>

  <template v-else-if="state.value === 'failure'">
    <p>{{ state.context.error.message }}</p>
    <button @click="send('RETRY')">Retry</button>
  </template>
</template>

<script>
import { assign, Machine } from 'xstate';
import { useMachine } from 'xstate-vue2';

const fetchMachine = createMachine({
  id: 'fetch',
  initial: 'idle',
  context: {
    data: undefined,
    error: undefined
  },
  states: {
    idle: {
      on: { FETCH: 'loading' }
    },
    loading: {
      invoke: {
        src: 'fetchData',
        onDone: {
          target: 'success',
          actions: assign({
            data: (_context, event) => event.data
          })
        },
        onError: {
          target: 'failure',
          actions: assign({
            error: (_context, event) => event.data
          })
        }
      }
    },
    success: {
      entry: 'notifySuccess',
      type: 'final'
    },
    failure: {
      on: {
        RETRY: 'loading'
      }
    }
  }
});

export default {
  props: {
    onResolve: {
      type: Function,
      default: () => {}
    }
  },
  setup(props) {
    const { state, send } = useMachine(fetchMachine, {
      actions: {
        notifySuccess: (ctx) => props.onResolve(ctx.data)
      },
      services: {
        fetchData: (_context, event) =>
          fetch(`some/api/${event.query}`).then((res) => res.json())
      }
    });
    return {
      state,
      send
    };
  }
};
</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(...):

<template>
  <div>
    <loader-idle v-if="state.matches('idle')" />
    <loader-loading-user v-if-else="state.matches({ loading: 'user' })" />
    <loader-loading-friends v-if-else="state.matches({ loading: 'friends' })" />
  </div>
</template>

Persisted and Rehydrated State

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

<script>
// Get the persisted state config object from somewhere, e.g. localStorage
const persistedState = JSON.parse(
  localStorage.getItem('some-persisted-state-key')
);

export default {
  setup() {
    const { state, send } = useMachine(someMachine, {
      state: persistedState
    });

    // state will initially be that persisted state, not the machine's initialState
    return { state, send };
  }
};
</script>