Quantcast
Channel: Active questions tagged svelte - Stack Overflow
Viewing all articles
Browse latest Browse all 1545

How do I wait for a store that will eventually appear on the context using @xstate/svelte?

$
0
0

I have an Authentication store based on Xstate that I am trying to set the actors based on the config I get from another machine...

export function createAuthMachine(fakeLogin) {    const chosenActors =        fakeLogin === "true" || fakeLogin === true ? fakeActors : realActors;return setup({        actions,        actors: chosenActors    }).createMachine(

The other machine (Config Machine) goes to get some global config stuff from the backend...

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;<slot {config}></slot>

Once this is done I then want to create an AuthMachine that uses the config to setup OAuth. In this case I am using @auth0/auth0-spa-js

import {getContext, onMount, setContext} from "svelte";import {useMachine} from "@xstate/svelte";import {createAuthMachine} from "$lib/auth/index.js";const globalConfigSnapshot = getContext('globalConfigSnapshot');...    $: if ($globalConfigSnapshot?.matches) {    if ($globalConfigSnapshot.matches('done')) {        const config = $globalConfigSnapshot.context.config;        if(config == null){            throw new Error("The Config cannot be null when creating the Auth Machine");        }        const authMachine = createAuthMachine(config.fakeLogin);        authService = useMachine(authMachine, {                input: config        });        setContext('authService', authService);    }}<slot/>

This all seems to work but the problem is I want to have a main page with the following logic...

  • If the authService has not been set on the MainPage state is loading.
  • If the authService is set wait for the authService state to be idle.
    • if the auth service is not set after 30 sec then Main page state should be set to failed
  • Once the state is idle the MainPage state should no longer be loading
    • if the authstate is Authenticated the MainPage state becomes authenticated
    • Otherwise the MainPage state is Unauthenticated

That leaves us with 4 possible main page states

  • Loading
  • Error
  • Authenticated
  • Unauthenticated

I tried to accomplish this like

<script>    import {getContext, onMount} from "svelte";    import {writable} from "svelte/store";    import {Dashboard} from "../dashboard/index.js";    import {Marketing} from "../marketing/index.js";    const MainState = {        LOADING: 'loading',        ERROR: 'error',        AUTHENTICATED: 'authenticated',        UNAUTHENTICATED: 'unauthenticated'    };    const authService = writable(null);    const MAX_RETRIES = 5;    const INITIAL_DELAY = 1000;    const hasGivenUp = writable(false);    const mainState = writable(MainState.LOADING);    const checkAuthService = () => {        const service = getContext("authService");        if (service) {            authService.set(service);            return true;        }        return false;    };    onMount(() => {        if (!checkAuthService()) {            let retryCount = 0;            let delay = INITIAL_DELAY;            const attemptConnection = () => {                if (retryCount >= MAX_RETRIES) {                    hasGivenUp.set(true);                    mainState.set(MainState.ERROR);                    return;                }                if (checkAuthService()) return;                retryCount++;                delay *= 2;                setTimeout(attemptConnection, delay);            };            attemptConnection();        }    });    $: {        if ($authSnapshot?.matches('idle')) {            mainState.set($authSnapshot.context.isAuthenticated ?                MainState.AUTHENTICATED :                MainState.UNAUTHENTICATED            );        }    }    // Only access snapshot and send when authService is available    $: serviceData = $authService || {        snapshot: {            subscribe: () => () => {            }        },        send: () => {        }    };    $: ({snapshot: authSnapshot, send} = serviceData);    const handleLogin = () => {        logMain('Login button clicked');        send({type: "LOGIN"});    };    const handleLogout = () => {        logMain('Logout button clicked');        send({type: "LOGOUT"});    };</script><section class="flex flex-col min-h-screen bg-marketing"><header class="bg-primary text-white shadow-xl p-4 flex justify-between items-center"><h1 class="text-xl font-bold">Welcome to Love Monkey</h1><nav>            {#if $mainState !== MainState.LOADING && $mainState !== MainState.ERROR}                {#if $mainState === MainState.UNAUTHENTICATED}<button class="btn btn-sm variant-ghost-surface" on:click={handleLogin}>                        Login</button>                {:else}<button class="btn btn-sm variant-ghost-surface" on:click={handleLogout}>                        Logout</button>                {/if}            {/if}</nav></header><main class="flex-grow">        {#if $mainState === MainState.LOADING}<div class="p-4">Loading...</div>        {:else if $mainState === MainState.ERROR}<Marketing/>        {:else}            {#if $mainState === MainState.AUTHENTICATED}<Dashboard/>            {:else}<Marketing/>            {/if}        {/if}</main></section>

But I get

Uncaught Error: https://svelte.dev/e/lifecycle_outside_component    at v (Main.svelte:26:25)    at P (Main.svelte:47:21)

Of course that tells me that this is not allowed...

onMount(() => {   ...   if (checkAuthService()) return; << This

and I understand that but I am unsure how to fix it. If I need to wait for something to eventually be on the context how do I delay the rendering until after that happens? Here is the wrapping code

<GlobalConfigProvider><AuthProvider><Main></Main></AuthProvider></GlobalConfigProvider>

#Update

I also tried updating the page to not render till it is available...

<script>    import GlobalConfigProvider from "../components/config/GlobalConfigProvider.svelte";    import AuthProvider from "../components/auth/AuthProvider.svelte";    import Main from "./main/Main.svelte";    import { getContext } from "svelte";    import { writable } from "svelte/store";    import { onMount } from "svelte";    const isAuthServiceReady = writable(false);    const showFallback = writable(false);    let fallbackTimer;    const POLLING_INTERVAL = 1000; // Check every 500ms    const MAX_ATTEMPTS = 10; // Maximum number of attempts    let attempts = 0;    function checkAuthService() {        const service = getContext("authService");        isAuthServiceReady.set(!!service);    }    onMount(() => {        fallbackTimer = setInterval(() => {            console.log('[Page] Checking auth service ready:', $isAuthServiceReady, 'attempt:', attempts);            if ($isAuthServiceReady) {                clearInterval(fallbackTimer);                console.log('[Page] Auth service is ready');                return;            }            attempts++;            if (attempts >= MAX_ATTEMPTS) {                clearInterval(fallbackTimer);                console.log('[Page] Max attempts reached, showing fallback');                showFallback.set(true);            }        }, POLLING_INTERVAL);        return () => {            if (fallbackTimer) clearInterval(fallbackTimer);        };    });    $: {        checkAuthService();    }</script>

However, that doesn't seem to work the logs I get are

[Auth Machine] Initializing client...AuthProvider.svelte:45 [AuthProvider] Auth machine created and startedindex.js:22 [Auth Machine] Idle...+page.svelte:23 [Page] Checking auth service ready: false attempt: 0+page.svelte:23 [Page] Checking auth service ready: false attempt: 1+page.svelte:23 [Page] Checking auth service ready: false attempt: 2+page.svelte:23 [Page] Checking auth service ready: false attempt: 3+page.svelte:23 [Page] Checking auth service ready: false attempt: 4+page.svelte:23 [Page] Checking auth service ready: false attempt: 5+page.svelte:23 [Page] Checking auth service ready: false attempt: 6+page.svelte:23 [Page] Checking auth service ready: false attempt: 7+page.svelte:23 [Page] Checking auth service ready: false attempt: 8+page.svelte:23 [Page] Checking auth service ready: false attempt: 9+page.svelte:34 [Page] Max attempts reached, showing fallbackMain.svelte:16 [Main Page] Auth snapshot update: "initializeClient"

Viewing all articles
Browse latest Browse all 1545

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>