Search code examples
async-awaitsveltesappersvelte-3

Svelte {#await}..{:then} block duplicating html with new data


I'm trying to use Sveltes {#await}..{:then} block to show NASA's image of the day but I'm getting a strange intermittent outcome. On the first page load the image and data loads in just fine. When I change the date using the date-picker on the page, it's supposed to replace the current image and description with the image and description for the selected date that is retrieved asynchronously. However, what's happening is sometimes the html with the new data just get's appended to the bottom of the page so the image and description for the previously selected date is still there.

Can anyone tell me how I can make sure the previous data is removed? Or could this be some kind of race condition?

<script>
    import { fade } from 'svelte/transition';
    import Loader from '../components/Loader.svelte';
    import {getContext} from 'svelte';
    import { format, parseISO } from 'date-fns'

    let todaysDate =  format(new Date(), 'y-MM-dd');
    let selectedDate = todaysDate;

    async function getPhotoOfTheDay() {
        let data = "";
        if (selectedDate !== todaysDate) {
            let response = await fetch(`https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&date=` + selectedDate);
            data = await response.json();
        } else {
            console.log("use in memory data");
            data = getContext('dailyImage');
        }
        return data;
    }

    $: imageData = getPhotoOfTheDay(selectedDate);
    $: formattedDate = format(parseISO(selectedDate), 'MMMM d, y')
</script>

<svelte:head>
    <title>All About Space: Photo of the day</title>
</svelte:head>

<div id="content">
    <h1>Photo of the day on {formattedDate}</h1>
    <p class="instructions">Choose a day to see the photo of the day for that date:
        <input type="date" bind:value="{selectedDate}" max="{todaysDate}" >
    </p>

    {#if imageData===""}
        <p>error.. no data for the selected date</p>
    {:else}
        {#await imageData}
            <Loader show="true"/>
        {:then image}
            <div class="image-result" transition:fade="{{duration: 300}}">
                <h2>{image.title}</h2>
                <p>
                    <img src="{image.url}" alt="{image.title}" title="{image.title}"/>
                    {image.explanation}
                </p>
            </div>
        {:catch error}

        {error.message}

        {/await}

    {/if}
</div>

<style lang="stylus">
    #content {
        background: rgba(0,0,0,0.8);
        backdrop-filter: blur(6px);
        border-radius: 5px;
        width: 75%;
        margin: 0 auto;
        padding: 1rem 2rem;

        &:after {
            clear: both;
            content: "";
            display: block;
            width: 100%;
        }
    }

    .instructions {
        border-bottom: 1px solid gray;
        padding-bottom: 2rem;
    }

    input {
        padding: 0.5rem 1rem;
        font-size: 1rem;
        cursor: pointer;
        border: 0;
        border-radius: 3px;
    }

    img {
        float: left;
        padding: 0 1rem 1rem 0;
        max-width: 50%;
    }
</style>

Solution

  • There is an active bug for transitions in await blocks. It seems to be your issue: https://github.com/sveltejs/svelte/issues/1591