Skip to content

Vue Integration

In Vue 3, use reactive wrappers or ref-based subscriptions with AFSM.

Basic usage

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { FSM, ChangeState } from 'afsm'
class Counter extends FSM {
count = 0
@ChangeState('idle', 'counting')
async increment() {
this.count++
}
}
const counter = new Counter('counter')
const state = ref(counter.state.toString())
const onChange = (newState: any) => {
state.value = newState.toString()
}
onMounted(() => counter.on(FSM.STATECHANGED, onChange))
onUnmounted(() => counter.off(FSM.STATECHANGED, onChange))
</script>
<template>
<p>state: {{ state }}</p>
<p>count: {{ counter.count }}</p>
<button @click="counter.increment()">+1</button>
</template>

Composable

Wrap a useFSM:

import { ref, onMounted, onUnmounted, type Ref } from 'vue'
import { FSM, type State } from 'afsm'
export function useFSMState(fsm: FSM): Ref<string> {
const state = ref(fsm.state.toString())
const cb = (newState: State) => {
state.value = newState.toString()
}
onMounted(() => fsm.on(FSM.STATECHANGED, cb))
onUnmounted(() => fsm.off(FSM.STATECHANGED, cb))
return state
}

Usage:

<script setup lang="ts">
const state = useFSMState(counter)
</script>
<template>
<p>{{ state }}</p>
</template>

Intermediate states & UI

<template>
<button :disabled="state === 'logining'" @click="auth.login(token)">
{{ state === 'logining' ? 'Logging in...' : 'Login' }}
</button>
</template>

Pinia integration

AFSM coexists with Pinia — let FSM manage state machines, Pinia manage global stores:

import { defineStore } from 'pinia'
import { FSM, ChangeState } from 'afsm'
class AuthFSM extends FSM {
user?: User
@ChangeState(FSM.INIT, 'loggedIn')
async login(token: string) {
this.user = await fetchUser(token)
}
}
export const useAuthStore = defineStore('auth', () => {
const auth = new AuthFSM('auth')
return { auth }
})

This site’s Playground is Vue

The Playground component on this site is built with Vue 3 + AFSM:

<!-- simplified -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { FSM } from 'afsm'
const fsm = ref<FSM | null>(null)
const history = ref<any[]>([])
function run(example: any) {
const inst = example.create()
inst.on(FSM.STATECHANGED, (newState, oldState) => {
history.value.push({ newState, oldState, time: Date.now() })
})
fsm.value = inst
example.run(inst)
}
</script>

Full source on GitHub.

Next steps