How to use await blocks and update state in Svelte

Viewed 1802

Expanding on the example from https://svelte.dev/tutorial/await-blocks, what is the conventional way to update numbers and use await blocks, or should await blocks be avoided altogether?

<script>
    let numbers = [1, 2, 3]
    async function getRandomNumber() {
        const res = await fetch(`tutorial/random-number`);
        const text = await res.text();

        if (res.ok) {
            return text;
        } else {
            throw new Error(text);
        }
    }
    
    let promise = getRandomNumber();

    function handleClick() {
        promise = getRandomNumber();
    }
</script>

<button on:click={handleClick}>
    generate random number
</button>

{#each numbers as number}
<p>
    {number}
</p>
{/each}
{#await promise}
    <p>...waiting</p>
{:catch error}
    <p style="color: red">{error.message}</p>
{/await}
2 Answers

Svelte just needs to be aware of the assignment, you can use the spread operator to create a new array or you can just call assignment.

// option 1
const newNumber = await promise;
numbers.push(newNumber);
numbers = numbers;

Or

// option 2
const newNumber = await promise;
numbers = [...numbers, newNumber]

Personally I think option 1 is the way to go as it doesn't allocate any memory to create a new array but rather modifies the existing array. Then the assignment just lets the compiler know that you modified the array.

See Svelte Docs: https://svelte.dev/docs#2_Assignments_are_reactive

Regarding using {#await} blocks in your example is purely for notifying the user that something is going on. You have numbers which is an array you want to add data to and a promise which gets more data. There isn't a super clean way to make the await block do the work for of modifying numbers for you but there's also no reason to avoid using the block.

For Example, this would not work to update the data:

{#await promise}
    <p>...waiting</p>
{:then data}
   <!-- html markup here is the convention -->
   <p> {numbers = [...numbers, data]} </p>
{:catch error}
    <p style="color: red">{error.message}</p>
{/await}

Data updates should be done in the JS while blocks should handle display logic. You can see the above reads kinda weird

Not sure if this is the conventional way, but updating handleClick as follows seems to do the trick:

async function handleClick() {
    promise = getRandomNumber();
    const newNumber = await promise
    numbers = [...numbers, newNumber]
}

See full result at Svelte REPL.

Related