Search code examples
eventssveltetauri

Array in Svelte not reactive from Tauri listen(), even with $:


I have this code, which displays a list of messages and should add a message that was sent by a Tauri event and update the list:

    let messages: Message[] = [];
    $: sortedMessages = [...messages].sort((a, b) => a.created - b.created);
.
.
.
    async function getMessages() {
        const newMessages = <Message[]> await invoke('get_messages', {userId: currentChatInfo?.user.id});
        messages = <Message[]>[...messages, ...newMessages];
    }

    listen('new-message', (event) => {
        const message: Message = <Message> event.payload;
        if(message.userId === currentChatInfo?.user.id) {
            messages = [...messages, message];
        }
    });
.
.
.
            {#await getMessages()}
                    <div>loading...</div>
            {:then sortedMessages}
                {#each sortedMessages as message}
                    <MessageBox {message}/>
                {/each}
            {/await}

but the update/re-render doesn't happen. And I don't see why. According to the docs and StackOverflow

$: sortedMessages = [...messages].sort((a, b) => a.created - b.created);

should do the trick. But it doesn't :(

I start to suspect it's the {#await} around the {#each}. But I'm not sure how to test that easily. And what to do if that's the case. I don't like the idea of passing the messages as a prop to this component. As I like how self-contained it is.

Edit: Turns out I was partially right with my suspicion, see @Corrl's answer for the details.


Solution

  • {:then sortedMessages}
    

    creates a new variable in the scope of the block with the result of the first getMessages() call
    Replace with

    {:then _}
    

    so that sortedMessages inside the each block points to the reactive variable