When working with VueJS and Vuex, using the splice method to replace an item (object) in an array stored in Vuex does not trigger a re-render of the

I have an array of records. Each record consists of an object with _id (mongo id), title, and value (value is an object with amount and currency).

When displaying the list of records using v-for, the ':key' for each item in the list is set to the id of the record.

The issue arises when trying to edit a record by changing the title or value.amount; the list does not re-render to reflect the changes even after updating the record in the store (using records.splice(idx, 1, updatedRecord))

Here's My Code: Record List

<template>
  <ion-list>
    <ion-item>
      <ion-label>Date</ion-label>
      <ion-label>Title</ion-label>
      <ion-label>Value</ion-label>
      <ion-label />
    </ion-item>
    <RecordPreview
      v-for="record in records"
      :key="record._id"
      :record="record"
      @onEditRecord="onEditRecord"
      @onDeleteRecord="onDeleteRecord"
    />
  </ion-list>
</template>

<script>
import { IonList, IonItem, IonLabel } from '@ionic/vue';
import RecordPreview from './RecordPreview.vue';

export default {
  name: 'RecordList',
  components: {
    RecordPreview,
    IonList,
    IonItem,
    IonLabel,
  },
  props: {
    records: {
      type: Array,
      default: () => [],
    },
  },
  emits: ['onEditRecord', 'onDeleteRecord'],
  setup(_, { emit }) {
    function onEditRecord(record) {
      emit('onEditRecord', record);
    }
    function onDeleteRecord(record) {
      emit('onDeleteRecord', record);
    }
    return {
      onEditRecord,
      onDeleteRecord,
    };
  },
};
</script>

Vuex record module

import recordService from '../../services/record.service';

export const recordModule = {
  state: () => ({
    records: [],
  }),
  actions: {
    async getRecords({ commit }) {
      const records = await recordService.getRecords();
      commit('setRecords', records);
    },
    async addRecord({ commit }, record) {
      const newRecord = await recordService.addRecord(record);
      commit('addRecord', newRecord);
    },
    async updateRecord({ commit }, record) {
      const res = await recordService.updateRecord(record);
      if (res.modifiedCount > 0) {
        commit('updateRecord', record);
      }
    },
    async removeRecord({ commit }, recordId) {
      const res = await recordService.removeRecord(recordId);
      if (res.deletedCount > 0) {
        commit('removeRecord', recordId);
        return true;
      }
      return false;
    },
  },
  mutations: {
    setRecords(state, records) {
      state.records = records;
    },
    addRecord(state, record) {
      state.records.push(record);
    },
    updateRecord(state, updatedRecord) {
      const recordIdx = state.records.findIndex((rec) => rec._id === updatedRecord._id);
      state.records.splice(recordIdx, 1, updatedRecord);
    },
    removeRecord(state, recordId) {
      const recordIdx = state.records.findIndex((rec) => rec._id === recordId);
      state.records.splice(recordIdx, 1);
    },
  },
  getters: {
    records(state) {
      return state.records;
    },
  },
};

RecordPreview

<template>
  <ion-item>
    <ion-label>
      {{ date }}
    </ion-label>
    <ion-label>
      {{ title }}
    </ion-label>
    <ion-label :color="amount > 0 ? 'success' : 'danger'">
      {{ value }}
    </ion-label>
    <ion-label>
      <ion-button
        color="warning"
        fill="outline"
        @click="onEditRecord"
      >
        Edit
      </ion-button>
      <ion-button
        color="danger"
        fill="outline"
        @click="onDeleteRecord"
      >
        Delete
      </ion-button>
    </ion-label>
  </ion-item>
</template>

<script>
import { computed } from '@vue/reactivity';
import { IonItem, IonLabel, IonButton } from '@ionic/vue';
import dayjs from 'dayjs';

export default {
  name: 'RecordPreview',
  components: {
    IonItem,
    IonLabel,
    IonButton,
  },
  props: {
    record: {
      type: Object,
      default: () => {},
    },
  },
  emits: ['onEditRecord', 'onDeleteRecord'],
  setup(props, { emit }) {
    const value = computed(() => `${props.record.value.currency} ${props.record.value.amount}`);
    const date = computed(() => dayjs(props.record.date).format('DD-MM-YY'));
    function onEditRecord() {
      emit('onEditRecord', props.record);
    }
    function onDeleteRecord() {
      emit('onDeleteRecord', props.record);
    }
    return {
      amount: props.record.value.amount,
      title: props.record.title,
      date,
      value,
      onEditRecord,
      onDeleteRecord,
    };
  },
};
</script>

To retrieve the records, I am using a getter from the store placed in the parent component of RecordList like this:

    const records = computed(() => store.getters.records);

package.json

{
  "version": "0.0.1",
  "private": true,
  ...
}


P.S. I attempted to create deep copies of the array and replace the entire array. I also tried creating a deep copy of the record, making changes, and replacing it. I suspect that the issue lies with the key used in the list rendering, as changing the ':key' attribute from 'record._id' to just 'record' triggers the list to re-render when replacing an item (this might be due to the change in the pointer reference of the object)

Answer №1

When retrieving data from an API, my suggestion is to refresh by fetching directly from the API. If you need to update the item in your state, consider using this method:

modifyData(state, updatedInfo) {
  const { name, content } = updatedInfo;
  let infoIndex = state.data.find((info) => info._id === updatedInfo._id);

  infoIndex.name = name;
  infoIndex.content = content;
  ...
},

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

Grant permission to access a website even when domain filtering is enabled

I recently created a basic web application that utilizes the YouTube API for performing search and displaying results. $.ajax({ url: 'http://gdata.youtube.com/feeds/mobile/videos?alt=json-in-script&q=' + q, dataType: 'jsonp&apos ...

Node.js data querying methods and best practices for code structuring

Recently delved into the world of nodejs and still fairly new to JavaScript. In my quest for best practices, I stumbled upon this helpful resource: Currently, I am working on an application following the structure recommended in the resource. The suggesti ...

What could be causing the issue with the custom Button component not functioning correctly on hover when using MUI Button alongside Tooltip

After creating a Custom Button that envelops a MUI Button to handle default props and other aspects, I encountered an issue that persisted despite simplifying the example. Below is the code snippet along with a link to a codesandbox for reference: CodeSand ...

Error: Class cannot be loaded by React Webpack loader

I'm encountering an issue with my two typescript packages - a React application and an infrastructure package. The React app has a dependency on the infrastructure package (currently linked via npm). After adding a new class to the infrastructure pack ...

Incorporating Common Types for Multiple Uses

Is there a way to efficiently store and reuse typings for multiple React components that share the same props? Consider the following: before: import * as React from 'react'; interface AnotherButtonProps { disabled?: boolean; onClick: (ev ...

Using Jquery to find the class that matches the current element

Is it possible to select a specific div by its class when there are multiple divs sharing the same class and triggering the same function on button click? I only want to target the class where $(this) is located. I've experimented with .parent, .chil ...

By default, Nuxt 2.15.7 is automatically installed when you create a new Nuxt app

I am looking to develop a Nuxt app (version 3) using Vue. However, when I run the command npm create nuxt-app@version mousa, it automatically installs Nuxt2. How can I install Nuxt3 instead with this command? ...

Route parameter in JavaScript: only accept numerical values

Is it possible to restrict parameter types to only accept digits? If a letter or other character is inputted, I want to fallback to a default scenario. Below are the two attempts I've made. app.get('/multi/:num?', function (request, respons ...

Can you tell me the alternatives for getServerSideProps and getStaticProps in Next.js version 14?

I'm trying to wrap my head around the rendering behavior of SSR/SSG/ISR in Next.js version 14 with the updated app router. Previously, Next.js provided predefined functions like getServerSideProps for server-side fetching and processing (SSR), or getS ...

What is the process for incorporating JavaScript files into an Angular project?

I have a template that works perfectly fine on Visual Studio. However, when I try to use it on my Angular project, I encounter an issue with the JavaScript code. I have filled the app.component.html file with the corresponding HTML code and imported the ...

A guide on utilizing Socket.io to efficiently transfer data to a specific client within a designated chat room

Can someone please advise on the correct way to send data to a specific client in a specific room using socket io? The code snippet I have been trying is: I am using the following command: socket.to(socket.id).emit('change', {data}) However, i ...

Having trouble viewing the CSS menu on Internet Explorer 8? Could it be a potential JavaScript issue causing the problem?

Receiving complaints about the menu bar dysfunction in Internet Explorer 8 has left me bewildered. Despite working perfectly on all other browsers and earlier versions of IE, I cannot figure out what's wrong with the code. You can view the website at ...

The componentDidUpdate method is functioning by comparing the previous state with the current state on different triggers

I have a primary element that is invoking another element with specific attributes. // Primary Element <SecondaryElement className="EnterNumber-input" submitClicked={this.state.submitClicked} /> Upon clicking a button, I am modify ...

The ng-repeat function is not functioning properly when used within an li element to trigger

I have utilized the Dialog service to create a pop-up window. My intention is to display a message to the user in this format: txt = '<ul> <li data-ng-repeat = "eachValue in dummnyList" > {{eachValue | applyFilte ...

Marked checkboxes and Node.js

I'm having trouble grasping the concept of using HTML checkboxes with Node.js and Express. I have a basic form in EJS and before diving deeper into the backend logic, I want to ensure that the correct values are being retrieved. Despite my efforts to ...

The discrepancy between the heights of a div using Jquery and JavaScript

There is a container div encompassing all the site's content, which dynamically stretches. Additionally, there are multiple other divs that also stretch using the same method as in 20 other sites. Despite trying various methods with jQuery and JavaSc ...

Tips for restricting users from inputting spaces into a form field using ReactJS

Within my application, I have developed a specialized component named QueryForm that serves the purpose of enabling search capabilities. The code snippet being outputted by this component is as follows: ...

Issues with Firebase-admin preventing writing to the database are occurring without any error messages being displayed

Issues with Firebase admin writing to the database. The database is instantiated as follows: var db = admin.database(); A reference to the desired table is then set up: var systemsRef = db.ref("systems/"); A function is created to check if a 'sys ...

What could be causing my Node application to give a 404 error when making a POST request?

I'm at a loss trying to debug my app for what seems like a simple error, but I just can't locate it. Here is an overview of how my Express app is structured. My routes are set up as follows: var routes = require('./routes'); ... app.g ...

Show the JSON data that was returned

I'm encountering a problem trying to access and display the returned object. Since it's cross-domain, I'm using jsonp, but I'm unable to retrieve the returned object for some reason. $(function(){ var API = "https://ratesjson.fxcm. ...