What could be causing my template to not update when it's bound to a computed property?

Currently, I am encountering an issue where a component's template HTML depends on the computed getter of a Vuex method. The template is designed to display the output of the computed property within a <p> tag using {{ getNumSets }}.

Upon updating the state with the UPDATE_EXERCISE_SETS mutation, I can verify through Vue devtools that the state updates correctly. However, this change is not reflected in the

<p> {{ getNumSets }} </p>
section.

Below is the snippet of the Template HTML:

<template>
...
<v-text-field
   v-model="getNumSets"
   placeholder="S"
   type="number"
   outlined
   dense
></v-text-field>
<p>{{ getNumSets }}</p>
...
</template>

The Component Logic portion looks like this:

<script>
...
computed: {
   getNumSets: {
      get() {
         var numSets = this.$store.getters['designer/getNumSetsForExercise']({id: this.id, parent: this.parent})
         return numSets
      },
      set(value) {  // This successfully updates the state according to Vue DevTools
        this.$store.commit('designer/UPDATE_EXERCISE_SETS', {
                    id: this.exerciseId,
                    parentName: this.parent,
                    numSets: parseInt(value),
                    date: this.date
                })
      }

}
...
</script>

Concerning the Vuex Store Logic:

...
state: {
  designerBucket: []
},
getters: {
  getNumSetsForExercise: (state) => (payload) => {
    var numSets = 0
    for (var i = 0; i < state.designerBucket.length; i++) {
      if (state.designerBucket[i].id == payload.id) {
        numSets = state.designerBucket[i].numSets
      }
    }
    return numSets
  }
},
mutations: {
  UPDATE_EXERCISE_SETS(state, payload) {
    state.designerBucket.forEach(exercise => {
       if (exercise.id == payload.id) {
          exercise.numSets = payload.numSets
       }
    })
   }
}

Your insights and suggestions are highly appreciated!

P.S. I have also experimented with utilizing a for (var i=0...) loop, iterating over indices, and then implementing Vue.set() to update the value. Although this approach did modify the store value, the computed property still fails to update the template accordingly.

Answer №1

Apologies for the lengthy response, but please bear with me.

I have a theory: it seems that Vue is not updating your computed property when there are state changes because you're returning a function from your Vuex getter. Even if the value returned by the function changes, the function itself remains the same, disrupting the caching mechanism for computed properties.


Reactivity Concerning Arrow Function Getters


It's important to note that when you create a getter that returns an arrow function like this:

getNumSetsForExercise: (state) => (payload) => {
    var numSets = 0
    for (var i = 0; i < state.designerBucket.length; i++) {
        if (state.designerBucket[i].id == payload.id) {
        numSets = state.designerBucket[i].numSets
        }
    }
    return numSets
}

...you are no longer directly returning state data from the getter.

This setup works well when you need to access state data based on local component information without requiring Vue to detect changes. However, using this getter in a computed property might lead to the misconception that updating state will automatically update the getter, which is not the case due to how computed properties track dependencies and cache data.

Computed Property Caching and Dependency Detection


In Vue, computed properties are more sophisticated than they initially appear. They store their results and monitor reactive values they rely on to determine when to refresh the cached data.

Vue internally stores the calculated value of a computed property so that if you call the property again without altering its dependencies, it can retrieve the cached result instead of recalculating.

The issue in your scenario lies in dependency detection – Vue registers three dependencies from your getter:

get() {
    var numSets = this.$store.getters['designer/getNumSetsForExercise']({id: this.id, parent: this.parent})
    return numSets
},
  1. The getter:
    this.$store.getters['designer/getNumSetsForExercise']
  2. this.id
  3. this.parent

None of these values change when the <v-text-field> triggers your setter.

As a result, Vue does not detect any changes in dependencies and continues to serve the cached data rather than recalculating.

Solution Approach


If you encounter such dependency issues, it usually indicates room for enhancing the state design by either incorporating more data into state or restructuring it in some manner.

In this situation, unless maintaining designerBucket as an array is necessary for ordering purposes, transforming it into an object where each set is keyed by id could simplify the process and eliminate the need for the getter:

...
state: {
  designerBucket: {}
},
mutations: {
  UPDATE_EXERCISE_SETS(state, payload) {
    // Use $set since we are adding a new property to the object
    Vue.set(state.designerBucket, payload.id, payload.numSets);
  }
}

Now, you can directly access designerBucket from the state and fetch data by referencing this.id:

<script>
...
computed: {
    getNumSets: {
    get() {
        return this.$store.state.designerBucket[this.id];
    },
    set(value) {
        this.$store.commit('designer/UPDATE_EXERCISE_SETS', {
            id: this.exerciseId,
            parentName: this.parent,
            numSets: parseInt(value),
            date: this.date
        });
    }
}
...
</script>

This revision should enable Vue to accurately detect changes and resolve the previous issue of stale cached data.

Answer №2

To begin, ensure that you import mapGetters from 'vuex' at the top of your script tag.

import { mapGetters } from "vuex"

Next, in your computed object, include mapGetters and pass arguments to the getter method within the get() method as demonstrated below:

computed: {
   ...mapGetters('designer',['getNumSetsForExercise']),
   getNumSets: {
      get() {
         var numSets = this.getNumSetsForExercise({id: this.id, parent: this.parent})
         return numSets
      },
      set(value) {  
        this.$store.commit('designer/UPDATE_EXERCISE_SETS', {
                    id: this.exerciseId,
                    parentName: this.parent,
                    numSets: parseInt(value),
                    date: this.date
                })
      }

}

Finally, test if the changes are effective.

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

jQuery Autocomplete API Issue: Undefined

I've been attempting to implement a basic text box using the jQuery API from DevBridge. I followed instructions in this video tutorial to create my code. However, despite properly declaring scripts before the JS code and ensuring proper mappings, I&a ...

Tips for refreshing a D3.js bubble chart with live JSON data updates

Currently delving into d3 and experimenting with transforming a static bubble chart into a dynamic one that adjusts by removing or adding bubbles based on JSON changes. I am aiming to have the JSON file refreshed every 5 seconds to update the bubble chart ...

Accordion Box glitch detected in Firefox browser

I have been working on a JavaScript function that controls a slide up/down box. However, I've encountered some issues with Firefox as the box only opens and closes once before failing to work properly again. Upon further investigation, it seems that F ...

Conceal any elements that have a class containing specific text

In my HTML file, I have multiple <span> elements with the class of temp_val that include a 1 value which I need to hide. These elements are placed throughout the document. Below is an excerpt from my HTML code: <div class="row" style="float: lef ...

Appending the desired URL to the existing URL using an Ajax callback

Ever since I started working on a Drupal 7 project, I have been facing an issue with making an ajax call back. The problem arises when the URL I intend to use for the callback gets appended to the current page the user is viewing. It's quite puzzling ...

What's the reason for the icon not updating in the responsive mode?

I am venturing into the world of responsive web design for the first time. My aim is to create a website menu that drops down when the menu icon is clicked, using the onclick command in JavaScript. However, upon inspecting my browser, I noticed an uncaught ...

Map on leaflet not showing up

I followed the tutorial at http://leafletjs.com/examples/quick-start/ as instructed. After downloading the css and js files to my local directory, I expected to see a map but all I get is a gray background. Can anyone advise me on what might be missing? T ...

Retrieving selected values from a dynamic Primeng checkbox component

Within my Angular app, I am managing an array of questions. Each question includes a text field and an array of string options to choose from. The questions are retrieved dynamically from a service and can be updated over time. To allow users to select mul ...

Vue.js v-cloak lifecycle method

Currently, I am working on a project where I have styled v-cloak with display: none, and it is decorating the body. As a result, everything remains hidden until the Vue instance is ready. I have created a component that inserts a chart (using highcharts). ...

The image upload failed: the server could not locate the requested URL

I am completely new to working with Flask, and I'm currently in the process of creating a basic image uploading application. After comparing my code with various tutorials on how to build similar apps, it seems like everything is in place. However, w ...

Ineffectiveness of Three.js camera's lookat function

I've been trying to modify the camera's focus point using a Vector3, but for some reason, the camera keeps looking at the scene's origin. I'm a bit perplexed as to why this is happening, especially since all the examples I've come ...

Utilize HTML5 to enable fullscreen functionality for embedded SWF files

I have implemented a function that handles the click event of a button to toggle a DOM element into fullscreen mode. The function works well, but I am facing an issue when there is another div containing a swf inside the main div. var elem = document.getE ...

What is the reason for my Firestore listener consistently retrieving data from the server despite having offline persistence enabled?

Incorporating Firebase JavaScript Modular Web Version 9 SDK into my Vue 3 / TypeScript application. My understanding is that when utilizing real-time listeners with offline persistence in Firestore, the process should proceed as follows: Upon initializat ...

Is it detrimental to my search engine ranking if I utilize CSS display:none?

Imagine having valuable content that is initially hidden with CSS, and then using javascript to reveal it only when the user clicks on certain links. Even users without javascript enabled can access this content by following the links to a new page where i ...

Passing data between API tests in JavaScript

I'm encountering an issue where I need to create e2e api tests. The goal of the first test is to obtain a token for an unauthorized user, use that token in the method header for the second test to return a token for an authorized user, and then contin ...

Ajax-powered Datatables

I am a beginner to data tables and I am attempting to retrieve data from a JSON text file (test1.txt). Below is an excerpt of the data present in the file, which contains over 5000 entries: [{"0":"22352442","ID":"22352442","1":"22126303","PARENT":"2212630 ...

How can I create walls in ThreeJS using a path or 2D array?

I am faced with the task of creating a 3D house model, specifically the walls, using a 2D path or array that I receive from a FabricJS editor that I have developed. The specific type of data being transferred from the 2D to 3D views is not critical. My in ...

blurring out of an input field and a division element

I am currently working on creating a dropdown suggestion box that hides when the input field and dropdown box are no longer in focus. My HTML code: <input type="text" id="user_address"> <div id="user_address_sg">SUGGESTION</div> <di ...

Is it necessary for each React component to have its own individual stylesheet?

Looking for some advice on React as a newbie here. I'm wondering whether each React component should have its own stylesheet. For instance, if I have my main App component that gets rendered to the browser, is it sufficient to include a CSS file the ...

Tips for preventing directly mutating a prop within a recursive component

The child operates on its own copy of the prop data and can notify the parent using `$emit` when a change occurs. Imagine dealing with a recursive tree structure, like a file system for example: [ { type: 'dir', name: 'music', childr ...