Search code examples
javascriptvuejs3office-jsoffice-addins

Vue3 - Suspense waiting for async variable


I have below two components (parent and child)

.
├── Index.vue (Parent)
└── _GetShipment.vue (Child)

In the Index.vue I am trying to get the content of the e-mail body using Office.js's getAsync:

<script>
import Shipment from './_GetShipment.vue';
import { ref, defineComponent, Suspense } from 'vue';


export default defineComponent({
    setup() {
        const shipments = ref([]);

        shipments.value = Office.context.mailbox.item.getRegExMatchesByName('ShipmentNumbers');

        if (!shipments.value) {
            Office.context.mailbox.item.body.getAsync(
                "text",
                function (result) {
                    if (result.status === Office.AsyncResultStatus.Succeeded) {
                        let matches = result.value.match(/S\w{3}\d{8,9}/ig);
                        if(matches){
                            shipments.value = matches;
                        }
                    }
                }
            );
        }

        return {
            shipments,
        }
    },
    components: {
        Shipment
    },
})
</script>

<template>
    <Suspense>
        <Shipment :id="shipments[0]" />
        <template #fallback>
            Loading...
        </template>
    </Suspense>
</template>

In the child component, I fetch data using the async setup method:

<script>
import { ref } from 'vue';

export default {
    props: ['id'],
    async setup(props) {
        const shipment = ref();
        await fetch(route('getShipment', {id : props.id}))
            .then(response => response.json())
            .then(data => shipment.value = data);
        return { shipment };

    }
};
</script>
<template>
    <div>
        <pre>{{ JSON.stringify(shipment, null, 2) }}</pre>
    </div>
</template>

When navigating to my page, Index.vue, the Loading... fallback message from Suspense is being shown, and the async data from the child is also being loaded.however, the console also throws an error:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '0')

I guess this is because when Index is loaded, the shipments variable is not set, but will be set just after load.

If I render the child component using a v-if, like so:

<template>
    <Suspense>
        <Shipment v-if="shipments" :id="shipments[0]" />
        <template #fallback>
            Loading...
        </template>
    </Suspense>
</template>

Then the fallback loading message is not being shown. How can I call an async function (getAsync()) whilst still showing the loading message using Suspense?


Solution

  • The comment from Jaromanda X put me on the right track:

    not sure if :id="shipments?.[0]" would help

    When the component is loaded, the shipments variable is empty - as it is waiting for the async function getAsync to finish.

    Adding the code from Jaromanda X to the parent component:

    <Shipment :id="shipments?.[0]" />
    

    And then in the child component, I added this under the async setup(props) method:

    while(!props.id){
       await new Promise(resolve => setTimeout(resolve, 100));
    }
    

    This got rid of the error in the log, the loading message is successfully showing until the fetch is done.