Replies: 2 comments 2 replies
-
Did you manage to get it to work in the end? I have a similar issue where I'm trying to reuse a state machine in different pages but I get 'Function called outside component initialization error'. |
Beta Was this translation helpful? Give feedback.
-
Yeah, this is an Svelte issue. Regardless of the design of your machine, you can have it easily available to children components via the context API. // foo-machine.ts
export fooMachine = setup({/*...*/}).createMachine({/*...*/}); // actor-subscription.ts
// I will transform this interface into a class, to be inherited by other classes.
import type { ActorRefFrom, AnyActorLogic, SnapshotFrom } from 'xstate';
export interface ActorSubscription<TActorLogic extends AnyActorLogic> {
readonly actorRef: ActorRefFrom<TActorLogic>;
readonly send: ActorRefFrom<TActorLogic>['send'];
readonly snapshot: SnapshotFrom<TActorLogic>;
} // foo-context.svelte.ts => "svelte.ts" makes it reactive
import type { ActorSubscription } from './actor-subscription';
import { onDestroy } from 'svelte';
import { createActor } from 'xstate';
import type { fooMachine } from './foo-machine';
type FooSubscription = ActorSubscription<typeof fooMachine>;
export class FooContext implements FooSubscription {
readonly actorRef: FooSubscription['actorRef'];
readonly send: FooSubscription['send'];
#snapshot = $state() as FooSubscription['snapshot'];
get snapshot() {
return this.#snapshot;
}
constructor(actorLogic: typeof fooMachine) {
this.actorRef = createActor(actorLogic);
this.send = this.actorRef.send;
this.#snapshot = this.actorRef.getSnapshot();
const subscription = this.actorRef.subscribe((snapshot) => {
this.#snapshot = snapshot;
});
onDestroy(subscription.unsubscribe);
this.actorRef.start();
}
}
export const fooContextId = Symbol(); // get-foo-contex.ts
import { getContext } from 'svelte';
import type { FooContext } from './foo-context.svelte';
import { fooContextId } from './foo-context.svelte';
export function getFooContext() {
return getContext<FooContext>(fooContextId);
} <script lang="ts">
// <FooProvider>
import {setContext} from 'svelte';
import type {Snippet} from 'svelte';
import {FooContext, fooContextId, fooMachine} from './'
type Props = {
children?: Snippet;
}
let {children}: Props = $props();
const fooContext = new FooContext(fooMachine);
setContext(fooContextId, fooContext);
</script>
{@render children?.()} Then, everywhere inside the <script lang="ts">
import {getFooContext} from '../get-foo-context';
const foo = getFooContext();
foo.actorRef // properly typed
foo.send // properly typed
const bar = $derived.by(() => foo.snapshot.matches('bar'));
$effect(() => {
if (bar) {
doSomething();
} else {
doSomethingElse();
});
<script> |
Beta Was this translation helpful? Give feedback.
-
Perhaps I'm daft, but I can't quite find any documentation or figure out how to instantiate a global (auth) machine where I can check the current auth state in some component and send state changes to trigger services from different components. My machine config script:
and in my SignInForm component I am importing the default-exported service and calling
await authService.send("SIGNIN", { input: { login: email, password, rememberMe } })
but the console.log in the service never gets printed.In a UserMenu component, I'm importing the same
authService
and usingconst authState = useSelector(authService, (state) => state.context)
to check the user with$authState.user
which of course returnnull
.I'm sure I'm just overlooking something fundamental as I'm new to Xstate, but I'm definitely keen to get FSMs into my project. Any help or pointers in the right direction would be greatly appreciated.
Beta Was this translation helpful? Give feedback.
All reactions