Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue Composition Reactive properties are not updating in components

I'm building out a notification system and it's sorta working, but sorta not. I have the follow Composition function

const data = reactive({
    notifications: []
});
let notificationKey = 0;

export const useNotification = () => {
    const visibleNotifications = computed(() => {
        return data.notifications.slice().reverse();
    });

    const add = (notification: INotification) => {
        notification.key  = notificationKey++;
        notification.type = notification.type ?? 'success';

        notification.icon      = iconObject[notification.type];
        notification.iconColor = iconColorObject[notification.type];
        data.notifications.push(notification);

        notificationTimer[notification.key] = new Timer(() => {
            remove(notification.key);
        }, notificationTimeout);
    };

    const remove = (notificationKey: number) => {
        const notificationIndex = data.notifications.findIndex(notification => notification?.key === notificationKey);
        if (notificationTimer[notificationKey] !== undefined) {
            notificationTimer[notificationKey].stop();
        }
        if (notificationIndex > -1) {
            data.notifications.splice(notificationIndex, 1);
        }
    };

    const click = (notification: INotification) => {
       /// ... click code 
    };

    return {
        visibleNotifications,
        add,
        remove,
        click
    };
};

This is working (It's been simplified a little bit). Now, I have two entry points in Webpack. In one entry point (auth), I have the following code to load up a Vue Component for showing the Notification

 Promise.all([
    import(/* webpackChunkName: "Vue"*/ 'vue'),
    import(/* webpackChunkName: "@vue/composition-api"*/ '@vue/composition-api'),
    import(/* webpackChunkName: "Notifications"*/'components/Notifications.vue')
]).then((
    [
        { default: Vue },
        { default: VueCompositionAPI },
        { default: Notifications },
    ]) => {
    Vue.use(VueCompositionAPI);

    new Vue({
        render: h => h(Notifications)
    }).$mount('.notification-outer);
});

Now, this all works, and I add in there the following code

import { useNotification } from 'modules/compositionFunctions/notifications';
useNotification().add({
    title  : 'Error',
    message: 'This is an error notification',
    type   : 'error',
});

Then the notification shows as expectednotification image This is all happening inside the "auth" entry point, and the above is all typescript.

Now, if I go to my second entry point (editor), and in an existing JS file enter the following code

import(/* webpackChunkName: "useNotification"*/ 'modules/compositionFunctions/notifications').then(({ useNotification }) => {
    useNotification().add({
        title     : 'Block Deleted',
        message   : 'The block has been deleted',
        buttonText: 'Undo',
        buttonFunction () {
            undoDelete();
        }
    });
});

Then it "works", and by this I mean, all of the code from the useNotification function works. The add method will add it, (if I console log out the reactive property), and after 15000ms, the remove methods happens and I can add logs to show that. HOWEVER, the Vue component never sees this change. If I add a watch in the Vue Component, and log out as we go, the first notification (image above) will make JS log to the console, however, when adding it from the "editor" entry point, it won't do anything.

Vue Component JS

import { useNotification } from 'modules/compositionFunctions/notifications';
import { defineComponent } from '@vue/composition-api';

export default defineComponent({
    name : 'Notifications',
    setup () {
        const { visibleNotifications, remove, click: notificationClick } = useNotification();

        return {
            visibleNotifications,
            remove,
            notificationClick,
        };
    },
    watch: {
        visibleNotifications: (v) => {
            console.log(v);
        }
    }
});

PLEASE, someone, tell me they can help? This is starting to do my head in...

TIA

like image 540
LeeR Avatar asked Nov 07 '22 05:11

LeeR


1 Answers

Using the code provided in the GitHub repo, I added some console logging immediately before this part:

window.notifications = reactive({
   notifications: []
});

The logging was called twice. Once from entry1.hash.bundle.js and once from entry2.hash.bundle.js.

My understanding of Webpack is limited but it would seem that it is building those two entry-points to be self-contained and isn't expecting you to run both of them on the page at the same time.

I would have thought it would be possible to extract all the notification stuff to its own shared chunk, a bit like the vendors bundle. That said, I'm not really clear why you're using two entry-points rather than two chunks. My understanding is that entry-points should be one-per-page and then you split them up using chunks.

All that said, there is a quick hack to get it working:

if (!window.notifications) {
    window.notifications = reactive({
        notifications: []
    });
}

This ensures that the same array is being shared no matter how many times it gets run.

I should stress that I don't advocate the use of such a hack in production code. It just kicks the Webpack problems further down the road and pollutes the global namespace in the process. It does help to confirm what the problem is though.

like image 146
skirtle Avatar answered Nov 12 '22 20:11

skirtle