Search code examples
setintervalsveltesvelte-3svelte-componentsvelte-store

reload API call at intervals based on the current path via svelte-pathfinder


I'm using svelte-pathfinder to observe URL parameters that are then sent to a method which runs an async API call. I'd like to be able to re-run my API call based on the current path at an interval (for example, every 50 seconds).

stores.js (note that fetchContests is a method that runs a await fetch() call to an external API based on the pattern and query values from svelte-pathfinder:

// routing
export const resultStore = derived([pattern, query], ([$pattern, $query], set) => {
    if ($pattern('/search/') && $query.params.q) {
        new Promise((resolve) => {
            setTimeout(() => {
                fetchContests('title', $query.params.q, true)
                    .then(set);
                resolve()
            }, delay)
        })
    } else if ($pattern('/')) {
        new Promise((resolve) => {
            setTimeout(() => {
                fetchContests('contest_ids', dashboard, true)
                    .then(set);
                resolve({name: "testing"})
            }, delay)
        })
    }
}, []);

Results.svelte (where I display the results of the API call):

<script>
    // data
    export let promise;
    import { resultStore } from './../stores.js';

    // layout components
    import Contest from "./Contest.svelte";
</script>

{#await promise}
    <p>Loading contests</p>
{:then}
    <ul>
        {#each $resultStore as contest}
            <Contest contest="{contest}"/>
        {/each}
    </ul>
{:catch error}
    <p>Something went wrong: {error.message}</p>
{/await}

This works really well in a non-repetitive way:

  1. On initial page load, it runs an API call based on the current path
  2. Whenever the path changes (a link click or a form submission or whatever), it runs another API call with those parameters. Each call gets the appropriate data from the API.

What I haven't been able to do is add setInterval to this in a way that preserves the path. So if the path is http://localhost:8080/#!/search?q=governor and 50 seconds have elapsed, the interval would make an API call with those current parameters to retrieve updated data. If the path is http://localhost:8080/, or http://localhost:8080/#!/search?q=senator instead when the next 50 seconds have elapsed, it would do the same with those parameters.

Originally, I thought maybe I could add setInterval to the API call itself, which is an async function that runs await fetch. But this didn't work, it would run multiple API calls ignoring the current path and parameter settings. I thought maybe I could run it within resultStore instead (above), but then it only ran after 50 seconds instead of running on initial page load and then again after each 50 second interval.


Solution

  • When extracting the logic that should run every time pattern or query changes and after a given interval after that, I think this might match what you want to do?

    let delay = 50000
    
    // routing
    export const resultStore = derived([pattern, query], ([$pattern, $query], set) => {
    
        fetchAndSet($pattern, $query, set)
    
        const interval = setInterval(() => {
            fetchAndSet($pattern, $query, set)
        }, delay);
    
        //  If you return a function from the callback, it will be called when
        //  a) the callback runs again, or b) the last subscriber unsubscribes.
        return () => {
            clearInterval(interval);
        };
    }, []);
    
    
    function fetchAndSet($pattern, $query, set) {
        if ($pattern('/search/') && $query.params.q) {
            fetchContests('title', $query.params.q, true).then(set)     
        } else if ($pattern('/')) {
            fetchContests('contest_ids', dashboard, true).then(set) 
        }
    }