Why does Vue only change a specific array element without updating the DOM?

My curiosity is piqued by a puzzling issue with updating specific items in an array using Vue.js. The documentation cautions that:

Due to limitations in JavaScript, Vue cannot detect the following changes to an array:

When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue.

While this guideline is clear, I am left wondering about the reasoning behind it. A similar query was raised in another discussion (Vue computed issue - when will it compute again).

The code snippet from the aforementioned question is as follows:

// works well

data() {
  return {
    cart: {
      item: {
        nums: 10,
        price: 10,
      },
    },
  }
},
computed: {
  total() {
    return this.cart.item.nums * this.cart.item.price
  },
},
methods: {
  set() {
    //why it worked.
    this.cart.item = {
      nums: 5,
      price: 5,
    }
  },
},

// oops! not working!

data() {
  return {
    cart: [
      {
        nums: 10,
        price: 10,
      },
    ],
  }
},
computed: {
  total() {
    return this.cart[0].nums * this.cart[0].price
  },
},
methods: {
  set() {
    this.cart[0] = {
      nums: 5,
      price: 5,
    }
  },
},

The explanation provided in response to the question is intriguing:

total will be recalculated if this.cart is marked as changed, this.cart[0] is marked as changed or if this.cart[0].nums or this.cart[0].price is changed. The problem is that you are replacing the object in this.cart[0]. This means that this.cart[0].price and nums do not change, because those still point to the old object.

If I have replaced the object in this.cart[0], why isn't this.cart[0] marked as changed? Why do this.cart[0].price and nums still point to the old object? I definitely made changes to this.cart[0], right?

Furthermore, what sets apart the two scenarios considering that both involve object replacement? Why does one work while the other doesn't?

Answer №1

Vue makes it clear that JavaScript has a limitation when it comes to detecting changes in array elements. JavaScript can only detect changes in the size of arrays due to adding or removing elements, not individual element replacements.

Behind the scenes, Vue implements mechanisms to monitor object changes, which JavaScript does support. This means that Vue can track changes in array elements that are objects. However, Vue cannot detect when an array element is replaced, leading to potential issues.

The recommended solution is well-documented: use Vue.set() to update items in an array. By doing this, Vue can recognize changes in array elements and adjust its tracking accordingly.

To implement this solution, you can follow this example:

Vue.set(this.cart, 0, {nums: 5, price: 5});

Answer №2

In brief: Arrays do not exhibit reactive behavior when modified directly.

To assign a value to an array, consider using one of these methods:

  • Vue.set(array, index, value)
  • vm.$set(array, index, value)
  • array.splice(index, 1, value)

Within the Observer class, the constructor(value) differentiates between arrays and non-arrays:

if (Array.isArray(value)) {
  this.observeArray(value)
} else {
  this.walk(value)
}

What does this.observeArray(value: Array) actually do?

For arrays, it essentially invokes new Observer(value[index]) within the constructor(value[index]).

No getter or setter is implemented to monitor changes during this array iteration process.


Exploring the alternative path: this.walk(obj: Object):

It executes defineReactive(obj, keys[i]), which in simplified terms, accomplishes the following:

defineReactive(obj, key=keys[i]){
  let val = obj[key]
  Object.defineProperty(obj, key, {

    get(){
      dep.depend() 
      return val
    },
    set(newVal){
      val = newVal
      deb.notify()
    }
  })
}

Array prototypes are altered to utilize the Observer this.__ob__.deb for notifications.


An additional approach involves the use of set functionalities registered as:

When working with arrays, Vue.set(array, index, value) triggers array.splice(index, 1, value), which subsequently leads to ob.observeArray(value) and ob.deb.notify().

Answer №3

If you're looking for an alternative to using Vue.set() for your set() method, you can try the following approach:

set() {
    this.cart.splice(0, 1, {
      quantity: 5,
      cost: 5,
    });
  },

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

typescriptCreating a custom useFetch hook with TypeScript and Axios

I have a query regarding the utilization of the useFetch hook with TypeScript and Axios. I came across an example of the useFetch hook in JavaScript, but I need help adapting it for TypeScript. The JavaScript implementation only handles response and error ...

Why is the click function being invoked twice, but exclusively on the initial click?

In my current project, I am facing an issue with the onClick action that is being passed down from Context. Strangely, when this action is clicked for the first time, it fires twice. However, from the second click onwards, it functions normally and only fi ...

What could be causing the createReadStream function to send a corrupted file?

My current task involves generating a file from a URL using the fs module to my local system. Initially, everything seems successful. However, when attempting to post this file into a group using the createReadStream() function, I encounter an issue where ...

Issue with 1.bundle.js not loading during webpack production build with routing

I have been encountering an issue with my test repository for this specific problem (Link) It appears that the problem lies within the localization file, as I am using react-intl. The development version seems to be functioning properly. This is what&ap ...

Vue combined with modules for namespace management

I seem to be facing a problem that I thought I had solved before by using namespaces, but unfortunately nothing seems to be working. Here's my module: const Algo1Module = { namespaced: true, state: { questions: { question1: "test&qu ...

The npm system is encountering difficulties in parsing the package.json file

Having recently started using npm and node, I decided to create a react app with truffle unbox react using npm init react-app. Despite attempting to reinstall npm and clear the cache multiple times, I consistently encounter an error when trying to run sudo ...

Modify the CSS style of the select label and change the color of the currently selected option in MUI

For the past few days, I've been facing challenges with implementing MUI components while working on a page for a React app. I'm almost finished with my project, but there are 2 key things missing... On my page, I have utilized a Select and an ...

Having trouble with material-ui installation in React, Redux, and React-Router project

Guide: https://i.stack.imgur.com/k1UMV.png Due to using redux and react router, incorporating MuiThemeProvider at the top of the chain is a bit challenging. What would be the most effective method to integrate this particular library? This is my ReactDO ...

jQuery.get() function is limited to specific types of webpages

I have successfully combined multiple weather APIs on my website, which can be found here. Recently, I started using the weather.gov API and it has been quite effective. However, there are certain data points that I need to extract which the weather.gov A ...

The disappearance of hashtag (#) when passed as req.query in the backend has been observed

I am facing an issue where a string with a hashtag in the req.query is not being parsed correctly as JSON. http://localhost:3000/link/?items=[{"quantity":1,"_id":"00001","box":"item01","desc":&quo ...

Why is the function app.get('/') not triggering? The problem seems to be related to cookies and user authentication

Need help with app.get('/') not being called I am working on implementing cookies to allow multiple users to be logged in simultaneously. Currently, users can log in successfully. However, upon refreshing the page, all users get logged in as the ...

Execute asynchronous JavaScript request

When a user types something into the input id=2, an ajax function triggers. Here is the HTML: <input id="2" type="text" onkeyup="posttitulo(this.value)" /> And here is the SCRIPT: function posttitulo(value){ $.post("getdata/posttitulo.php",{p ...

The function Event.preventDefault seems ineffective when attempting to block input of CJK (Korean) characters in a v-text-field of type

Currently, I am tackling an issue in a Vue project where I need to fix a small bug. However, the solution seems quite challenging. I have managed to make the v-text-field accept only numerical input, which is functioning well. <v-text-field type=" ...

Variable in Javascript file causing return value to be 'undefined'

I have encountered an issue with my JavaScript file. It is extracting data from a SharePoint list and displaying it on an HTML page, but one of the fields appears as 'undefined' even though I defined it initially. The problematic variable is &ap ...

Is there a way to utilize the child component's method?

I am looking to access a child component's method from the parent in Vue.js. To achieve this, I plan on using $refs. Code Example: <template> <div>Parent!</div> </template> Script: <script> Vue.component('c ...

Embarking on the GSAP journey

I'm attempting my first animation using GSAP, but no matter what I try, nothing seems to be working. I've even tried using example code without success. Within my PHP file, I have the following code snippet: <head> <script src="https:/ ...

Incorporating Node.JS variables within an HTML document

After building a simple site using Express, I discovered that Node.js variables can also be used with Jade. exports.index = function(req, res){ res.render('index', { title: 'Express' }); }; This is the code for the index file: ext ...

What is the process for including a JavaScript file in an HTML document?

Greetings to all and thank you for taking the time! I have a straightforward query for you.. I have designed an html page featuring only a basemap (open street map). Moreover, I possess a javascript file which utilizes your coordinates to calculate a perce ...

Hold on until the page is reloaded: React

My current setup includes a React Component that contains a button. When this button is clicked, a sidePane is opened. What I want to achieve is refreshing the page first, waiting until it's completely refreshed, and then opening the sidepane. Below i ...

Executing a VueJS getter function in the browser console - a step-by-step guide

I'm trying to debug a VueJS component and I want to test the behavior of a getter in the browser. Can I manually call it from the browser console? Update: This link provides information on how to trigger a state change manually. In particular: You ...