Anna
Anna

Reputation: 2845

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

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.

Upvotes: 5

Views: 6400

Answers (3)

Madacol
Madacol

Reputation: 4296

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"

Upvotes: 3

Stephane Vanraes
Stephane Vanraes

Reputation: 16451

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))

Upvotes: 6

johannchopin
johannchopin

Reputation: 14873

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.

Upvotes: 2

Related Questions