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

Angular - Manipulating the selected option in a select box from the controller

My issue involves a select box that is defined in the following manner: <select ng-model="selectedSupplier" ng-options="supplier.name for supplier in suppliers"> </select> Within my controller, there is a button that doesn't have any r ...

Slide feature in Drupal Views allows you to create dynamic

This is the design I currently have: https://i.stack.imgur.com/P6spc.jpg For example, when you click on the "Structure" header, it opens up the contents and shows an image. I have created a content type and installed Views. I have separated the image, h ...

Step-by-step guide to installing gatsby-CLI on Windows without requiring admin permissions

Currently, I am facing an issue while trying to install the gatsby CLI using the following npm command: npm install --global gatsby-cli I suspect the problem might be due to lack of admin access. Does anyone have suggestions on how to resolve this error? ...

Is it possible to display a value conditionally based on a condition from a Json object?

I'm looking to add a new feature that displays all the movies featuring Sean Connery in a button, but I'm stuck on how to implement it. Prettier 2.7.1 --parser babel Input: import React, { useState } from "react"; import styled ...

Webstorm showcases files with similar content in a distinct manner

In Webstorm, everything is color-coded based on whether it is a function, object, etc. I am working with JavaScript. I have noticed that in two different files, the same line of code looks differently: var Comment = React.createClass({ In file 1: "var" ...

Using websockets in a React client application

Attempting to establish a connection with a backend layer running on localhost, here is the provided source code: const { createServer } = require("http"); const cors = require("cors"); const photos = require("./photos"); const app = require("express")( ...

`validate.js verifying the elements within an array`

One of the challenges I'm facing in my JavaScript project is dealing with objects that have two array properties included. As part of my development process, I've decided to utilize the resources provided by the validate.js library. To illustrat ...

accordions that are supposed to adapt to different screen sizes are

My custom responsive accordions are not functioning as expected. The requirement is to display headings and content for desktop view, but switch to accordions on smaller devices. I have managed to do this, however, when resizing the window, the functionali ...

Implementing b-tooltip from Bootstrap-vue on a b-table

Utilizing bootstrap-vue to display data on a b-table, I have truncated long text and display the original when hovering over it using the title prop. It works well with custom CSS, but I would like to implement b-tooltip. <b-table hover sticky-hea ...

How does Chrome have the capability to access the gist json file? Isn't that typically not allowed?

Fetching a JSON file from Github Gist can sometimes be straightforward, but in certain situations, I have faced difficulties due to CORS restrictions. This has led me to resort to using JSONP instead. Can you shed some light on why this is the case? ...

Node development does not operate continuously

I'm facing a minor issue with node-dev. I followed the instructions in the readme file and successfully installed it. However, when I run the command like so: node-dev somescript.js, it only runs once as if I used regular node without -dev. It doesn&a ...

Ajax disregards the endless scroll material that has been injected into the DOM

I am currently facing an issue with loading posts on a page using foreach and the paginate function in Laravel. I have set up infinite scrolling through the posts, with each post having its own unique form. The first posts that load without JavaScript scro ...

JavaScript Slider for Color Selection

In my slider, I have added a .images class along with buttons for previous and next. To set the colors, I have used JavaScript to define an array like this: let colors = ['red', 'green',]; Currently, clicking the next-button displays ...

What is the best way to incorporate the value of a textbox into a list?

I currently have a list structured like this: <p>Please choose from the following options:</p> <script type="text/javascript"></script> <select id='pre-selected-options1' multiple='multiple'> <opt ...

Is it appropriate to include a function within a loop?

I'm curious to know if it's considered good practice to call a function inside a loop. Both of the following code snippets produce the same result, but I want to add clarity by using a function. Is this considered good practice? Thank you. Code ...

Navigating errors during the distribution of numerous messages with SendGrid and Node.js

I have developed a command line application that interacts with a DynamoDB table to extract email addresses for items that have not yet received an email. The process involves creating customized message objects, sending emails using SendGrid's sgMail ...

Adjust the background color of children based on the parent's mouseover event

Seeking a solution to fill three bars with varying percentages: <div class="bars" style="width:0%; background-color: red;"> <span class="bar"></span> <span class="bar"></span> <span class="bar"></span> </ ...

The glitch in VueJS's array updating functionality

I am facing an issue where I have an array called tiles with a title. On creating the instance, I add a field to this array named active. Subsequently, I display this array within an <li> element and iterate through it to display the title and activ ...

Formik integration issue with MUI DatePicker causing error message: "date.isBefore is not a function"

I'm currently creating a form in React using Formik and MUI components. However, I encountered an error that says: date.isBefore is not a function TypeError: date.isBefore is not a function at DayjsUtils.isBeforeDay (http://localhost:3000/static/j ...

What is preventing me from displaying the results on the webpage?

Currently, I am utilizing Express alongside SQLite and SQLite3 modules. However, upon running the server, the data fails to display on the initial request. It is only after a page refresh that the data finally appears. I attempted a potential solution by ...