Bug: Svelte's snapshot store out of sync
XState version
XState version 5
Description
I am finishing to upgrade XState to v5 in a project using SvelteKit 4. However, I am finding some problems that I didn't have when using XState v4.
Previously, I had many occurrences like this, and they used to work fine.
<script>
import {useMachine} from '@xstate/svelte';
import {goto} from '$app/navigation';
import {fooMachine} from './foo.ts';
const {state} = useMachine(fooMachine);
</script>
{#if $state.matches('active')}
<slot />
{:else}
{#await goto('/somewhere/else')}
Loading
{/await}
{/if}Now, with v5, the $snapshot store does not appear to work properly, or at least not fast enough because I get endless loops of redirection.
For fixing them, I had to do this.
<script>
import {useActor, useSelector} from '@xstate/svelte';
import {goto} from '$app/navigation';
import {fooMachine} from './foo.ts';
const {actorRef} = useActor(fooMachine);
const isActive = useSelector(actorRef, (snapshot) => {
return snapshot.matches('active');
});
</script>
{#if $isActive}
<slot />
{:else}
{#await goto('/somewhere/else')}
Loading
{/await}
{/if}Expected result
The $sessionSnapshot store, returned by useActor(), should match the state active.
Actual result
The $sessionSnapshot store remains in its initial state: inactive. However, a new subscription using useSelector() is updated.
Reproduction
https://codesandbox.io/p/github/phcoliveira/xstate-snapshot-problem/main?import=true
Additional context
The main files to checkout are:
src/lib/require-activated/require-activated.svelte
src/routes/activating/+page.svelteBut please do look around.
There are ways to circumvent this. For example, having the promise inside the logic of the machine and sending an event usually works too.
But I included in the first file a strange fix that uses useSelector instead of using the snapshot returned by useActor.
I can not really confirm that it used to work that way with XState 4 because, back then, I had these many actors (services, back then) inside one machine. When upgrading to XState 5, I dismembered them in order to make smaller machines and reduce the initial bundle size.
Source: statelyai/xstate