Search code examples
sveltesveltekit

Consume REST API and use response data inside multiple components (Not page)


I have a page with multiple components, like tables and displayed in one page.

My +page.svelte:

<script>
.
.
<script>
<div class="page-content">
<Row>
  <Table1 />
  <List /> -->
  <Table2 />
</Row>
</div>

My REST API call as an example in one of my component:

<script>
let todos // 'forever' undefined

    async function fetchTodos() {       
        const response = await fetch('https://jsonplaceholder.typicode.com/todos/')
        return await response.json()
    }
</script>

{#await fetchTodos() then todos}
    <h1>{todos.length} todos!</h1>
{/await}

However if I want to use it inside each tables and some of the data in the List component, according to this solution I need to place the same call all of my component and I generate unnecessary extra same calls.

  1. What is the proper way to call the endpoint once and reuse the response data in all components?
  2. I want to poll the REST API call in every 15 seconds and refresh the fields inside the component, so saving extra call would be crucial. Any polling solution?

Solution

  • For sharing state I would recommend using a context (setContext/getContext), these are available in an entire component sub-tree. E.g. use it in a layout that all relevant pages use.

    To make the context reactive, you can use a store. For polling you can just use setInterval but that should be cleaned up once the data is no longer required; stores can have a StartStopNotifier to start polling on first subscription and stop on last unsubscribe.

    Note that you can also load data directly in a layout which then could be used to initialize the store.

    Basic example without initial data:

    // lib/data-store.js
    import { writable } from 'svelte/store';
    import { browser } from '$app/environment';
    
    // ...
    
    export function createDataStore() {
      const store = writable(null, (set, update) => {
        if (browser == false)
            return;
    
        async function getData() {
            const newData = await fetchNewData();
            set(newData); // or use update to e.g. append to existing data
        }
    
        const handle = setInterval(getData, 5000);
        getData();
    
        return () => clearInterval(handle);
      });
    
      return store;
    }
    
    <!-- +layout.svelte -->
    <script>
        import { createDataStore } from '$lib/data-store';
        import { setContext } from 'svelte';
    
        setContext('data', createDataStore());
    </script>
    
    <slot />
    
    <!-- some component in sub-tree -->
    <script>
        import { getContext } from 'svelte';
    
        const store = getContext('data');
    </script>
    
    {$store}
    

    REPL

    (To keep polling independent of subscribers you can use onMount to start and its returned function to stop. E.g. if you need a consistent stream of data.)