You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在@xstate/svelte中等待上下文动态生成的AuthService问题

问题:Svelte上下文动态注入AuthService后的状态等待逻辑实现

我基于Xstate实现了认证存储(Authentication store),会根据Config Machine从后端获取的配置选择actors创建AuthMachine,并将其服务存入Svelte上下文。现在需要在主页面实现以下逻辑:

  • 未获取到authService时显示加载
  • 30秒未获取到则显示错误
  • 获取到后等待其进入idle状态,再根据认证状态显示对应页面

尝试两种方案均失败:

  1. 在onMount中调用getContext触发Svelte生命周期错误
  2. 轮询检测authService始终返回false,最终触发错误fallback

日志显示AuthMachine已创建,但页面无法检测到上下文里的authService。页面结构为 <GlobalConfigProvider><AuthProvider><Main></Main></AuthProvider></GlobalConfigProvider>,求正确实现方式。

相关代码片段

export function createAuthMachine(fakeLogin) {
    const chosenActors = fakeLogin === "true" || fakeLogin === true ? fakeActors : realActors;
    return setup({ actions, actors: chosenActors }).createMachine(
<!-- GlobalConfigProvider -->
<script>
import { useMachine } from '@xstate/svelte';
import { setContext } from 'svelte';
import {globalConfigMachine} from "../../lib/config/index.mjs";

const { snapshot, send } = useMachine(globalConfigMachine);

$: if ($snapshot.matches('done')) {
    setContext('globalConfig', $snapshot.context.config);
}
setContext('globalConfigSnapshot', snapshot);

export let config = $snapshot.context.config;
</script>

<slot {config}></slot>
<!-- AuthProvider -->
<script>
import {getContext, setContext} from "svelte";
import {useMachine} from "@xstate/svelte";
import {createAuthMachine} from "$lib/auth/index.js";
const globalConfigSnapshot = getContext('globalConfigSnapshot');
...    
$: if ($globalConfigSnapshot?.matches?.('done')) {
    const config = $globalConfigSnapshot.context.config;
    const authMachine = createAuthMachine(config.fakeLogin);
    authService = useMachine(authMachine, { input: config });
    setContext('authService', authService);
}
</script>

<slot/>

解决方案

核心问题分析

Svelte的getContext是同步获取逻辑,而你的AuthService是异步注入的(依赖GlobalConfigMachine完成配置加载),直接同步获取或简单轮询无法响应上下文的动态更新。

正确实现步骤

1. 优化AuthProvider的上下文注入逻辑

用可写变量作为中间层,实现上下文的动态更新通知,避免直接在反应式语句中调用setContext:

<!-- AuthProvider -->
<script>
import { getContext, setContext, writable } from "svelte";
import { useMachine } from "@xstate/svelte";
import { createAuthMachine } from "$lib/auth/index.js";

const globalConfigSnapshot = getContext('globalConfigSnapshot');
// 创建可写变量存储authService,用于后代组件订阅
const authServiceStore = writable(null);
setContext('authServiceStore', authServiceStore);

$: if ($globalConfigSnapshot?.matches?.('done')) {
    const config = $globalConfigSnapshot.context.config;
    const authMachine = createAuthMachine(config.fakeLogin);
    const authService = useMachine(authMachine, { input: config });
    // 更新可写变量,触发后代组件的响应式更新
    authServiceStore.set(authService);
}
</script>

<slot/>

2. 主页面(Main组件)实现完整状态逻辑

通过订阅可写变量捕获AuthService,结合定时器实现超时,同时用Xstate的waitFor等待指定状态:

<!-- Main组件 -->
<script>
import { getContext, onMount } from "svelte";

const authServiceStore = getContext('authServiceStore');
let authService = null;
let isLoading = true;
let hasError = false;
let timeoutId;

// 启动30秒超时定时器
function startTimeout() {
    timeoutId = setTimeout(() => {
        isLoading = false;
        hasError = true;
    }, 30000);
}

// 订阅authService的变化
authServiceStore.subscribe(async (service) => {
    if (service) {
        authService = service;
        // 取消超时定时器
        clearTimeout(timeoutId);
        isLoading = true;
        
        // 等待AuthMachine进入idle状态
        await service.waitFor(state => state.matches('idle'));
        isLoading = false;
    }
});

// 组件挂载时启动超时
onMount(() => {
    startTimeout();
    // 组件销毁时清理定时器
    return () => clearTimeout(timeoutId);
});
</script>

{#if hasError}
    <div>加载超时,请刷新页面重试</div>
{:else if isLoading}
    <div>加载中...</div>
{:else}
    {#if $authService.snapshot.matches('authenticated')}
        <div>已认证页面内容</div>
    {:else}
        <div>未认证页面内容</div>
    {/if}
{/if}

3. GlobalConfigProvider的小优化(可选)

将配置也存入可写变量,方便后代组件更灵活地订阅:

<!-- GlobalConfigProvider -->
<script>
import { useMachine } from '@xstate/svelte';
import { setContext, writable } from 'svelte';
import { globalConfigMachine } from "../../lib/config/index.mjs";

const { snapshot, send } = useMachine(globalConfigMachine);
setContext('globalConfigSnapshot', snapshot);

// 用可写变量存储配置
const globalConfigStore = writable(null);
$: if ($snapshot.matches('done')) {
    globalConfigStore.set($snapshot.context.config);
}
setContext('globalConfigStore', globalConfigStore);
</script>

<slot config={$globalConfigStore}/>

关键原理说明

  • Svelte上下文本身是静态的,无法直接更新,通过可写变量作为中间层,可以实现动态更新的通知
  • 订阅可写变量能准确捕获AuthService的创建时机,避免轮询的低效问题
  • Xstate的service.waitFor方法可以优雅等待机器进入指定状态,无需手动轮询状态变化

内容的提问来源于stack exchange,提问作者Jackie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:43:24