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)