javascriptsveltesvelte-transition

Prevent Svelte transition on first render after onMount


I'm working with the following code in Svelte 3.55.0.

First, I have an empty webpage because I have no items to render. Then, onMount fires and I fetch some items from an API. Finally, the items get rendered.

<script>
import { slide } from "svelte/transition";
import { onMount } from "svelte";

// Initially we have no items.
let items = [];
let id = 0;

onMount(() => {
    // Fetch items from API.
    items = [
        {id: id, name: `item ${id++}`},
        {id: id, name: `item ${id++}`},
        {id: id, name: `item ${id++}`},
    ];
});

function addItem() {
    items = [
        ...items,
        {id: id, name: `item ${id++}`},
    ];
}
</script>

<div>
    <button on:click={addItem}>add</button>
    {#each items as it (it.id)}
        <div transition:slide>
            <p>{it.id} {it.name}</p>
        </div>
    {/each}
</div>

The problem is that fetch gets like 50 items and I don't want to play transitions for any of them. However, I do want transitions when individual items are added or removed only inside of the list.

Is there a way to achieve this effect?

Sandbox: https://codesandbox.io/s/blue-shape-njxx9o?file=/App.svelte


Solution

  • This could be achieved by adding an #if block around the main element so it's only rendered after the items are fetched and adding the |local flag to the transition - tutorial

    REPL

    (Like here the transition only seem to play correctly if the outer element has display:flex)

    <script>
        import { slide } from "svelte/transition";
        import {onMount} from 'svelte'
    
        let items = [];
        let id = 0;
        let itemsFetched = false
    
        onMount(async() => {
            setTimeout(() => {
                items = [
                    {id: id, name: `item ${id++}`},
                    {id: id, name: `item ${id++}`},
                    {id: id, name: `item ${id++}`},             
                ];
                console.log('fetched')
                itemsFetched = true
            },1000)
        })
    
        function addItem() {
            items = [
                ...items,
                {id: id, name: `item ${id++}`},
            ];
        }
    </script>
    
    <button on:click={addItem}>add</button>
    
    {#if itemsFetched}
    <div id="outer">
        {#each items as it (it.id)}
        <div transition:slide|local={{duration: 2000}}>
            <p>{it.id} {it.name}</p>
        </div>
        {/each}
    </div>
    {/if}
    
    <style>
        #outer {
            display: flex;
            flex-direction: column;
        }
    </style>
    

    Alternative with #await REPL

    <script>
        import { slide } from "svelte/transition";
        import {onMount} from 'svelte'
    
        let items = [];
        let id = 0;
    
        function fetchItems() {
            return new Promise(res => {
                setTimeout(() => {
                    items = [
                        {id: id, name: `item ${id++}`},
                        {id: id, name: `item ${id++}`},
                        {id: id, name: `item ${id++}`},             
                    ];
                    console.log('fetched')
                    res()
                },1000)
            })      
        }
    
        function addItem() {
            items = [
                ...items,
                {id: id, name: `item ${id++}`},
            ];
        }
    </script>
    
    <button on:click={addItem}>add</button>
    
    {#await fetchItems() then _}
    <div id="outer">
        {#each items as it (it.id)}
        <div transition:slide|local={{duration: 2000}}>
            <p>{it.id} {it.name}</p>
        </div>
        {/each}
    </div>
    {/await}
    
    <style>
        #outer {
            display: flex;
            flex-direction: column;
        }
    </style>