javascriptvuejs3async-components

Vuejs 3: Force async component to re-fetch new data


I have simple app written in Vue 3, which shows different SVG images based on UID served by Vue router (menu clicks). Fetching SVG is more complicated, because based on tags in SVG, I'm changing internal structure and making it reactive with {{ store.value }} (refs to Pinia store). This is not important for this question, just explanation, why I'm creating component on the fly and not using just v-html construct.

<script setup>
    import {defineAsyncComponent, watch} from "vue";

    const props = defineProps({
        uid: {
            type: String,
            default: null
        }
    });

    watch(() => props.uid, () => {
        console.log('UID CHANGED');
    });

    const SvgComponent = defineAsyncComponent(() => {
        return new Promise((resolve, reject) => {
                    fetch('http://url/with/svg/' + props.uid)
                    .then(response => response.text())
                    .then((svg) => {
                        console.log('SVG FETCHED');
                        resolve({
                            setup() {
                                return  {
                                }
                            },
                            template: '<div>' + svg + '</div>'
                        })
                    })
        })
    });

</script>

<template>
    <SvgComponent :key="uid"/>
</template>

What is problem: When UID is changed, new data/svg is not fetched and image is not redrawn. I didn't find any method how to destroy/unmout/recreate component. Output of console is here:

SVG FETCHED
UID CHANGED
UID CHANGED
UID CHANGED

Can anyone help me with that?

Thank you!


Solution

  • Thanks to VUE discord, I found out, that solution is very simple. Just recreate component in watch:

    <script setup>
        import {defineAsyncComponent, watch} from "vue";
    
        const props = defineProps({
            uid: {
                type: String,
                default: null
            }
        });
    
        let SvgComponent = fetchSvg();
    
        watch(() => props.uid, () => {
            SvgComponent = fetchSvg()
        });
    
    
        function fetchSvg() {
            return defineAsyncComponent(() => {
                return new Promise((resolve, reject) => {
                    fetch('http://url/with/svg/' + props.uid)
                            .then(response => response.text())
                            .then((svg) => {
                                console.log('SVG FETCHED');
                                resolve({
                                    setup() {
                                        return  {
                                        }
                                    },
                                    template: '<div>' + svg + '</div>'
                                })
                            })
                })
            });
        }
    
    </script>
    

    You can also ommit :key in template