1
votes

I have a page (a dynamic route) where I am fetching data from an API in the load function. What is the correct way to show a loading indicator till the data is fetched. Things I have tried:

  1. Using await block. I return a promise to fetch function and then in the normal script tag, I export a promise variable. I then resolve this promise manually after manipulating the data.
<script context="module">
  export async function load({ fetch, page }) {   
    let collectionId = page.params.id;    
    let endpoint = url;   
    const promise = fetch(endpoint);  
    return {props:{promise}}; 
  }
</script>

then in normal script tag

<script>
export let promise = new Promise(() => '');

promise = new Promise((resolve, reject) => {
    promise.then(function (response) {
      if(response.ok){
        console.log('response');
        response.json().then(function (json) {
          console.log('data in promise');
          console.log(json);
          let posts = json.map((post) => new Post(post));
          posts = posts.sort(function (a, b) {
            return a.id - b.id;
          });
          resolve(posts);
        });
      }else{
        response.text().then((text)=>reject(text));
      }
    });
});
</script>

then in HTML

{#await promise}
    <Shimmer items="3" />
{:then posts}
    <Cards data={posts} />
{:catch error}
    <Error message={error}/>
{/await}

This works fine the first time, I am guessing, this works when the page is rendered server side. But on subsequent calls, my promise resolution logic doesn't get called and I receive promise directly in my await block, where there is no logic to manipulate it.

  1. I export a variable to receive final processed data from the load function and in HTML, I try to display the loading indicator till this variable is undefined using {if} block. This works for the first time when the variable is actually undefined, but on subsequent calls, only the value of this variable changes but it is never undefined.

let posts;

{#if posts===undefined}
Loading...
{:else}
{posts}
{/if}
2

2 Answers

0
votes

One of the perks of using the load() function in a module in this way is that you don't need to use any kind of spinner or await the data, since the function runs before the component is created. The page effectively doesn't load until the data is ready to go. From the docs:

[The load function] allows you to get data for a page without (for example) showing a loading spinner and fetching data in onMount.

So in your case, just using

<script>
    export let promise;
</script>

is enough. It shouldn't matter if it's the first time you're hitting that page or not, the data in your promise variable is available without having to use any await or promise logic on it. It's as simple as using if (promise) to see if you got the data.

0
votes

So I have solved this by using the navigating store. I don't know if that's the correct way. But this seems to be working fine for me.

After the page is server-rendered and the control passes to the client-side, navigating store is true while navigating from one page to other. (Read More)

So in my __layout.svelte, I am doing

{#if $navigating} Loading... {:else} Content {/if}