What is a svelte approach to showing a loader after a time of waiting?

Viewed 2378

To show a loading spinner while waiting for a web request response, I'd use the following very simple if statement with my spinner component:

{#if waitingForAPIResponse}
    <Spinner></Spinner>
{/if}

What is a good approach to only show the Spinner component after, say, 200ms of waiting? I intuitively want to set up a timer, but I bet there's a better svelte approach.

3 Answers

The most dynamic way to do this would be to add another promise to the mix.

<script>
    const wait = () => new Promise((res) => setTimeout(res , 1000))
</script>

{#await APIRequest}
    {#await wait()}
        <span>Not going to take long</span>
    {:then a}
        <span>Taking a while</span>
  {/await}
{:then data}
    ...
{/await}

Important here is that the 'inner await' calls a function, this will trigger the creation of a new promise everytime it is rendered (for instance if for some reason your retrigger the APIRequest).

This can also be expanded to be flexibile in how long you wait by simple passing an argument to wait:

const wait = delay => new Promise((res) => setTimeout(res, delay))

You could also include the waiting logic inside a Loader component

<!-- Loader.svelte -->

<script>
    import { onDestroy } from "svelte";

    let show = false;
    const timeoutId = setTimeout(()=>show=true, 400);
    onDestroy(()=>clearTimeout(timeoutId));
</script>

{#if show}
    <Spinner/>
{/if}

and then just use it like this

{#if waitingForAPIResponse}
    <Loader/>
{/if}

it won't render the <Spinner/> until the timeout is completed.


You can customize lots of things with this approach.
You could make the inner <Spinner/> dynamic by using slots

...

{#if show}
    <slot>
        <Spinner/>
    </slot>
{/if}

and now you could use it like this

{#if waitingForAPIResponse}
    <Loader>
        <Spinnerv2/>
    </Loader>
{/if}

Of course, it would not be a "Loader" anymore, more like a "DelayedRenderer"

I guess there is no easiest way to produce an event after x second with setTimeout. So I would just use another variable timerOK that is initially false and after 200ms true:

<script>
    import {onMount} from 'svelte'

    let response = null
    let timerOK = false
    
    $: isLoading = !(response && timerOK)
    
    onMount(() => {
        setTimeout(() => { 
            timerOK = true
        }, 1200)

        fetch('...').then(callResponse => {
            // do your stuffs
            response = 'OK'
        })
    })
</script>

{#if isLoading}
    ...Loading
{:else}
    {response}
{/if}

Have a look at the REPL.

Related