简体   繁体   中英

Use XState in a nuxt composable

I want to use an xstate state machine in Nuxt 3 which is used over multiple components. I created a small example of how I want this to look like.

I also use the nuxt-xstate module.

State Machine:

export default createMachine(
    {
        id: 'toggle',
        initial: 'switched_off',
        states: {
            switched_on: {
                on: {
                    SWITCH: {
                        target: 'switched_off'
                    }
                }
            },
            switched_off: {
                on: {
                    SWITCH: {
                        target: 'switched_on'
                    },
                }
                
            },
        },

    }

)

Composable:

const toggle = useMachine(toggleMachine)

export function useToggleMachine(){
    return { toggle }
}

app.vue:

<template>
  <div>
    State: {{toggle.state.value.value}}
  </div>
  <br />
  <button
    @click="toggle.send('SWITCH')"
  >
      Switch
  </button>
</template>

<script>
    import { useToggleMachine } from '~/composables/toggle_machine'
    export default { 
        setup(){
            const { toggle } = useToggleMachine()


            return { toggle }
        }
    }
</script>

The problem is, that I can have a look at the state of the machine {{state.value.value}} gives me the expected 'turned_off'. But I cannot call the events to transition between states. When clicking on the button, nothing happens.

Here is the console.log for the passed 'toggle' object:

在此处输入图像描述

Does anyone know a way how to fix this, or how to use xstate state machines over multiple components. I am aware that props work, but I don't really want to have an hierarchical approach like that.

In Nuxt3, it's very simple:

in composables/states.ts

import { createMachine, assign } from 'xstate';
import { useMachine } from '@xstate/vue';

const toggleMachine = createMachine({
  predictableActionArguments: true,
  id: 'toggle',
  initial: 'inactive',
  context: {
    count: 0,
  },
  states: {
    inactive: {
      on: { TOGGLE: 'active' },
    },
    active: {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      entry: assign({ count: (ctx: any) => ctx.count + 1 }),
      on: { TOGGLE: 'inactive' },
    },
  },
});
export const useToggleMachine = () => useMachine(toggleMachine);

In pages/counter.vue

<script setup>
const { state, send } = useToggleMachine()
</script>

<template>
  <div>
    <button @click="send('TOGGLE')">
      Click me ({{ state.matches("active") ? "✅" : "❌" }})
    </button>
    <code>
      Toggled
      <strong>{{ state.context.count }}</strong> times
    </code>
  </div>
</template>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM