How to use vue 3 suspense component with a composable correctly?

Viewed 892

I am using Vue-3 and Vite in my project. in one of my view pages called Articles.vue I used suspense component to show loading message until the data was prepared. Here is the code of Articles.vue:

<template>
<div class="container">
    <div class="row">
        <div>
            Articles menu here
        </div>
    </div>
    <!-- showing articles preview -->
    <div id="parentCard" class="row">
        <div v-if="error">
            {{ error }}
        </div>
        <div v-else>
            <suspense>
                <template #default>
                    <section v-for="item in articleArr" :key="item.id" class="col-md-4">
                        <ArticlePrev :articleInfo = "item"></ArticlePrev>
                    </section>
                </template>
                <template #fallback>
                    <div>Loading...</div>
                </template>
                
            </suspense>
        </div>
    </div> <!-- end of .row div -->
</div>
</template>

<script>
import DataRelated from '../composables/DataRelated.js'
import ArticlePrev from "../components/ArticlePrev.vue";
import { onErrorCaptured, ref } from "vue";
/* start export part */
export default {
    components: {
        ArticlePrev
    },
    
    setup (props) {
        const error = ref(null);
        onErrorCaptured(e => {
            error.value = e
        });

        const {
            articleArr
        } = DataRelated("src/assets/jsonData/articlesInfo.json");
        
        return {
            articleArr,
            error
        }
    }

} // end of export
</script>

<style scoped src="../assets/css/viewStyles/article.css"></style>

As you could see I used a composable js file called DataRelated.js in my page that is responsible for getting data (here from a json file). This is the code of that composable:

/* this is a javascript file that we could use in any vue component with the help of vue composition API */
import { ref } from 'vue'

export default function wholeFunc(urlData) {
  const articleArr = ref([]);
  const address = urlData;

  const getData = async (address) => { 
    const resp = await fetch(frontHost + address);
    const data = await resp.json();
    articleArr.value = data;
  }

  setTimeout(() => {
    getData(address);
  }, 2000);
 

  return {  
    articleArr
  }
} // end of export default

Because I am working on local-host, I used JavaScript setTimeout() method to delay the request to see that the loading message is shown or not. But unfortunately I think that the suspense component does not understand the logic of my code, because the data is shown after 2000ms and no message is shown until that time. Could anyone please help me that what is wrong in my code that does not work with suspense component?

1 Answers

It's a good practice to expose a promise so it could be chained. It's essential here, otherwise you'd need to re-create a promise by watching on articleArr state.

Don't use setTimeout outside the promise, if you need to make it longer, delay the promise itself.

It could be:

  const getData = async (address) => { 
    await new Promise(resolve => setTimeout(resolve, 2000);

    const resp = await fetch(frontHost + address);
    const data = await resp.json();
    articleArr.value = data;
  }
  const promise = getData(urlData)

  return {  
    articleArr,
    promise
  }

Then:

async setup (props) {
    ...
    const { articleArr, promise } = DataRelated(...);
    await promise 
    ...

If DataRelated is supposed to be used exclusively with suspense like that, it won't benefit from being a composable, a more straightforward way would be is to expose getData instead and make it return a promise of the result.

Related