What could be causing my Vue array watcher to not trigger when I add an item to the array?

Below is a brief 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("adding", 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
    })
}

Additionally, here is a Vue component named Toast that makes use of the above module:

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

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

</script>

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

The Toast component is incorporated into App.vue. It is observed to be rendered successfully (the text "I'm present" is visible in the browser.)

Upon calling showSuccess("TEST") from another section, the following events occur:

  1. adding {type: 'success', message: 'DOH'}
    is displayed in the console log
  2. subsequently, toasts modified does not appear on the console log, and no further action takes place

To my understanding, watchers are inherently deep (refer: https://vuejs.org/guide/essentials/watchers.html#deep-watchers ), implying that the watcher function should trigger upon an addition to the array.

What could possibly be incorrect in this implementation?

Answer №1

The concept of deep functionality is initially intended for objects only according to the Vue 3 migration guide. When it comes to arrays, the deep option needs to be explicitly specified in order to trigger mutation detection.

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

Answer №2

No need to delve deep into your array (unnecessary and costly), simply monitor its length:

observe(() => notifications.value.length, () => {
    console.log("notifications updated")
    while (notifications.value.length) {
        const settings = notifications.value.pop()!
        showToast(settings)
    }
})

Answer №3

Just wanted to point out a flaw in the example code and provide an alternative solution. It's important not to modify the watched object from the watcher, especially inside a loop that iterates over the watched object.

const displayToasts = () => {
    const copyOfToasts = [...toasts.value]; // create a copy of the array
    toasts.value = []; // clear the original array
    while (copyOfToasts.length) { // iterate over the copy
        const options = copyOfToasts.pop()!; 
        showPopup(options);
    }
}
const delayedDisplayToasts = debounce(displayToasts, 50);

// Ensure that the watched object is not modified within the watcher
watch(() => toasts.value.length, delayedDisplayToasts);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issues arise with Typescript compiler on Windows systems due to soft symlinks causing compilation failures

In my TypeScript project, symlinks function properly on both macOS and Linux. However, when executing tsc in git-bash on Windows (not within WSL), the files cannot be resolved by tsc. ...

Creating and incorporating a generator function within an interface and class: A step-by-step guide

In vanilla JavaScript, the code would look something like this: class Powers { *[Symbol.iterator]() { for(let i = 0; i < 10; i++) yield { i, pow: Math.pow(i, i) } return null; } } This can then be utilized in the following manner: co ...

Require a property to be mandatory depending on the value of another property within a generic interface

Looking for a way to have one property affect the others in a react component interface? Here's an example of what I'm trying to achieve: export interface IMyAwesomeComponentProps<T = {}> { className: string defaultPath?: ISomeOthe ...

Typescript - Stripping multiple characters from the start and end of a string/Retrieving attributes of a JSON list element

My challenge involves a string like the following : "{"element":"634634"}" My goal is to eliminate {"element":" which remains constant, as well as the final character "}. The only variable component is 634634. How can I achieve this? Alternatively, can ...

Typescript error: Unable to instantiate __WEBPACK_IMPORTED_MODULE_1_signature_pad__ as a constructor

Currently, I am working on a project using Angular2 and attempting to incorporate a JS library for signature input from https://github.com/szimek/signature_pad. In my code, I have tried utilizing the library in the following way: // .ts file import * as ...

Encountering the error message "React child is not valid as a Gatsby wrapRootElement" while using TypeScript with Gatsby

I've been exploring ways to implement a theme provider in Gatsby using the wrapRootElement browser API. However, I seem to have hit a roadblock as I keep encountering an error message that says "Objects are not valid as a React child (found: object wi ...

Issue in Typescript: "Implementing Partial is restricted to object types or intersection of object types with known members" error occurs when working with classes

Recently, I encountered an issue with my code that was previously working fine until I updated Typescript: class DefaultRouteConfig implements Partial<RouteConfig> { public meta = { layout: LayoutDefault }; } However, after the update, Typescript ...

Combining data types in TypeScript (incorporating new keys into an existing keyof type)

If I have a typescript type with keys: const anObject = {value1: '1', value2: '2', value3: '3'} type objectKeys = keyof typeof anObject and I want to add additional keys to the type without manually defining them, how can I ...

Utilize Typescript for managing sessions in MySQL database

I'm attempting to set up a mysql session storage in NestJS using Typescript. I've gone ahead and installed the necessary packages such as express-session, express-mysql-session, and @types/express-mysql-session. The code snippet below is compilin ...

Discovering the name of an object property by locating its corresponding id

I am working with a basic JSON data structure where I need to retrieve the name from an object by comparing its ID. For example, if I have the number 2, I need to check if it matches any object's ID. If the ID is equal to 2, then I must fetch the corr ...

Exclusive Vue3 Props that cannot be used together

How can a component be created that accepts either json with jsonParserRules or jsonUrl with jsonParserRulesUrl, but not both? It would be ideal if the IDE could provide a warning when both props are specified. Example of an Attempt that does not Work < ...

Design interactive images in NativeScript

I need assistance setting up clickable images in NativeScript. My goal is to arrange 5 images horizontally, and when one image is clicked, the images to its left should change their values. Here's what I've attempted: <Label col="0" row="0" ...

What exactly is the meaning of "Dispatch<SetStateAction<boolean>>" in React with TypeScript?

I am currently working on writing Frontend Tests for React Components in TypeScript. The code I am using belongs to a more seasoned programmer, so some of the data types are unfamiliar to me. One particular prop has been defined with the data type "Dispa ...

Having trouble deleting a component from an array in React?

I am facing an issue with my array and component functions. Each component has a handleRemove function that is supposed to remove the selected component from the array. However, the function is not working as expected. Instead of deleting just the selected ...

TypeScript is not compatible with the intended ECMAScript version

Out of the blue, it seems like my VS17 has started transpiling my TypeScript to ECMAScript 6, but now VS is not accepting it and throwing a bunch of SCRIPT1002 and SCRIPT1006 errors suddenly: "JavaScript critical error at line 3, column 5 in http://localho ...

Prevent the Vue page from loading until the data has been fetched successfully

I'm in the process of finding a way to prevent my page from loading until my fetch task is completed. I'm facing some issues that need to be addressed: I have to re-fetch the data each time because I can't reuse the same data. (Changing vie ...

Transform a standard array of strings into a union string literal in TypeScript

I'm currently developing a module where users can specify a list of "allowable types" to be utilized in other functions. However, I'm encountering challenges implementing this feature effectively with TypeScript: function initializeModule<T ex ...

Changing the title of your document and app bar in React Router when navigating pages

I'm looking into implementing the react router in a TypeScript project. How can I dynamically update the document title (document.title) and the app bar title (<span className="appbartitle">Home</span>) in a single page application based o ...

Issues with implementing AddEventListener in InAppBrowser on IONIC 2

I am currently working on implementing AddeventListener to listen for 'Exit' and 'LoadStart' events in InAppBrowser within IONIC2. Here is my HTML: <button (click)="browsersystem('https://www.google.com')" > Visit URL& ...

Having trouble extracting value from another component or dealing with an architectural issue?

Just recently delving into Angular, I embarked on this journey last week with a small programming foundation. My current project involves creating a simple blog application where I need to pass a value from the root (app.component.ts) to my component tag " ...