typescriptvuejs3vue-component

Why is my vue array watcher not called when I push an item into the array


Here is a small typescript module:

import { ref } from 'vue'
import { i18n } from '~/i18n'
import { ElMessageBoxOptions } from 'element-plus'

const { t } = i18n.global

export const toasts = ref<ElMessageBoxOptions[]>([])

export const showToast = async (options: ElMessageBoxOptions) => {
    console.log("pushing", options)
    toasts.value.push(options)
}

export const showError = async (e: any) => {
    var message : string
    try {
        message = t("" + e)
    } catch (e) {
        console.log(""+e)
        message = t("general.something_strange_happened")
    }
    showToast({
        type: 'error',
        message: t('general.error') + "  " + message,
    })
}

export const showSuccess = async (message: string) => {
    showToast({
        type: 'success',
        message: message
    })
}

And here is a vue component called Toast that uses it:

<script setup lang="ts">
import { watch } from 'vue'
import { ElMessageBox } from "element-plus";
import { toasts } from '~/lib/toast'

watch(toasts, () => {
    console.log("toasts changed")
    while (toasts.value.length) {
        const opts = toasts.value.pop()!
        ElMessageBox(opts)
    }
})

</script>

<template>
    <span>I'm here</span>
</template>

The Toast component is added in App.vue. I can see that it is rendered (the "I'm here text is shown in the browser.)

When I call showSuccess("TEST") from another component, then the following happens:

  1. pushing {type: 'success', message: 'DOH'} appears in the console log
  2. then toasts changed is NOT SHOWN in the console log, and nothing else happens

AFAIK watchers are deep by default ( see https://vuejs.org/guide/essentials/watchers.html#deep-watchers ), and the watcher function should be called when an item is pushed into the array.

What am I doing wrong?


Solution

  • The deep functionality is by default for objects only. This is something originally mentioned in the Vue 3 migration guide. Specific to arrays, "To trigger on mutation, the deep option must be specified."

    watch(toasts, () => {
        console.log("toasts changed")
        while (toasts.value.length) {
            const opts = toasts.value.pop()!
            ElMessageBox(opts)
        }
    }, { deep: true })