vuejs3mountvuex4

Passing Vuex store in manually mounted Vue 3 component


This Vue 2 solution was what I have been using to mount a Vue 2 component manually, but as I am trying to uplift the project to Vue 3 (using a compatibility build for migration) I am finding that this method of instantiating and mounting a new component won't work anymore.

I am wondering if anyone has had any luck converting this method of manual mounting components from Vue 2 to Vue 3 and have come across this issue.

Here is my Vue 2 code for mounting the component, using the 'new' keyword and passing props with 'propsData' and this$store with 'store'. This worked great in Vue 2.

mountLegendGroup (legendElement, indicators, panel) {
    const Component = LegendGroup;
    const legendGroup = new Component({
        propsData: {
            indicators: indicators.map(i => i.params), 
            isUpper: panel.isUpper,panelUid: 
            panel.params.uid
        },
        store: this.$store
    });
    legendElement.innerHTML = '';
    legendGroup.$mount(legendElement, true);
},

Here is my Vue 3 code for mounting the component, using the 'mount-vue-component' package.

mountLegendGroup (legendElement, indicators, panel) {
    const Component = LegendGroup;
    Component.props = ['indicators', 'isUpper', 'panelUid'];
    legendElement.innerHTML = '';
    mount(Component, { 
        props: { 
            'indicators': indicators.map(i => i.params), 
            'isUpper': panel.isUpper, 
            'panelUid': panel.params.uid
        } 
    }, legendElement);
}

Here is the component I am trying to instantiate and mount, which has been imported in the parent component in both examples of the mounting method

<template>
    <div>
        <div class="legend-group">
            <div class="left-group">
                <div
                    v-for="(indicator, index) in indicators"
                    :key="index"
                >
                    <LegendItem
                        :key="index"
                        :indicator="indicator"
                        :panel-uid="panelUid"
                    />
                </div>
            </div>
            <div class="right-group">
                <LegendMovePanel
                    v-if="isMovable"
                    :panel-uid="panelUid"
                />
                <LegendExpandCollapse
                    v-if="showExpandCollapse"
                    :panel-uid="panelUid"
                />
                <LowerOverlayDropdown
                    v-if="!isUpper"
                    :panel-uid="panelUid"
                />
            </div>
        </div>
    </div>
</template>

<script>
import { mapGetters } from 'vuex';

import LegendExpandCollapse from '@/components/modcharts/LegendExpandCollapse';
import LegendItem from '@/components/modcharts/LegendItem';
import LegendMovePanel from '@/components/modcharts/LegendMovePanel';
import LowerOverlayDropdown from '@/components/modcharts/LowerOverlayDropdown';

export default {
    components: {
        LegendExpandCollapse,
        LegendItem,
        LegendMovePanel,
        LowerOverlayDropdown
    },
    computed: {
        ...mapGetters([
            'activeTheme',
            'chartGetter',
            'options'
        ]),
        isMovable () {
            const chart = this.chartGetter();
            return !this.isUpper && chart.panels.length > 2;
        },
        panelComputedHeightsEnabled () {
            return this.options.usePanelComputedHeight;
        },
        resizablePanelsEnabled () {
            return this.options.panelResize;
        },
        showExpandCollapse () {
            return (
                !this.isUpper &&
                !this.panelComputedHeightsEnabled &&
                !this.resizablePanelsEnabled
            );
        }
    },
    props: {
        indicators: {
            type: Array,
            required: true
        },
        isUpper: {
            type: Boolean,
            default: false
        },
        panelUid: {
            type: String,
            required: true
        }
    }
};
</script>

Here are the two warnings [Vue warn]: Property "$store" was accessed during render but is not defined on instance. and [Vue warn]: Unhandled error during execution of render function coming from the newly mounted component.


Solution

  • Found the solution after a couple of days of poking around, seen below

    const legendGroup = createApp({
        render () {
            return h(LegendGroup, {
                indicators: indicators.map(i => i.params),
                isUpper: panel.isUpper,
                panelUid: panel.params.uid
            });
        }
    });
    legendGroup.use(this.$store);
    legendElement.innerHTML = '';
    legendGroup.mount(legendElement, true);