Tuesday 24 November 2020

How to run server-sent events in svelte component in sapper

I have a svelte component named [symbol].svelte in which I want to initiate a connection to a streaming service to receive server-sent events. I've not found a way to do this successfully.

Since EventSource only runs in the browser, I initialized it in the onMount function like so:

<script>
    export let quote;

    let sse = {};

    onMount(async () => {
        sse = new EventSource(`https://myurl.com?symbol=${quote.symbol}`);
        sse.onmessage = (event) => {
            let response = JSON.parse(event.data);
            if(!response.length) return;
            quote = response[0];
        }
    });

    onDestroy(() => {
        if(sse.readyState && sse.readyState === 1) {
            sse.close();
        }
    })  
</script>

<div>{quote.symbol}</div>

This works fine, except when I navigate to another route that uses the same component- since the component doesn't unmount and remount, onMount() doesn't fire and thus doesn't instantiate a new SSE request. I don't know of any way to easily force the component to remount, which would be simplest (relevant github issue here)

Another try was using a reactive statement like so:

<script>
    export let quote;

    let sse = {};

    $: {
        if(process.browser === true) { //again, this stuff won't run on the server
            if(sse.readyState && sse.readyState === 1) {
                sse.close();
            }
            sse = new EventSource(`https://myurl.com?symbol=${quote.symbol}`);
        }
    }

    sse.onmessage = (event) => {
        let response = JSON.parse(event.data);
        quote = response[0];
        console.log(quote);
    }
</script>

<div>{quote.symbol}</div>

When changing routes, the quote variable changed, thus triggering the reactive statement to kill the existing SSE and instantiate a new one. Exceptthe onmessage handler wouldn't fire, probably because the onmessage handler gets attached before the eventsource object is created.

Last take was to try with the onmessage handler in the reactive statement like so:

<script>
    export let quote;

    let sse = {};

    $: {
        if(process.browser === true) { //again, this stuff won't run on the server
            if(sse.readyState && sse.readyState === 1) {
                sse.close();
            }
            sse = new EventSource(`https://myurl.com?symbol=${quote.symbol}`);
            sse.onmessage = (event) => {
                let response = JSON.parse(event.data);
                quote = response[0];
                console.log(quote);
            }           
        }
    }
</script>

<div>{quote.symbol}</div>

The problem here is that since quote gets reassigned as a product of the onmessage handler, the reactive statement keeps firing circularly.

At this point I'm at a loss, any input would be appreciated!



from How to run server-sent events in svelte component in sapper

No comments:

Post a Comment