I built the following simple UI.
On clicking the trash icon, the bookmark should be deleted and the UI updated, because of the state change. The API call is made, and I can see in the dev tools, that the action takes place. However, I have to either merge the action or navigate away from the page or do a hard reload for the deleted bookmark not to show up. I expected this to work through the usage of vuex's mapState helper.
Below are the relevant parts.
view (sorry, this is a little messy) - this is actually the unabridged version:
<template>
<div>
<v-card class="mx-auto" max-width="700">
<v-list two-line subheader>
<v-subheader>Bookmarks</v-subheader>
<v-list-item
v-for="obj in Object.entries(bookmarks).sort((a, b) => {
return a[1].paragraph - b[1].paragraph;
})"
:key="obj[0]"
>
<v-list-item-avatar>
<v-icon @click="goTo(obj)">mdi-bookmark</v-icon>
</v-list-item-avatar>
<v-list-item-content @click="goTo(obj)">
<v-list-item-title>
{{ obj[0].split('/')[1] + ' by ' + obj[0].split('/')[0] }}
</v-list-item-title>
<v-list-item-subtitle>
Part {{ obj[1].part + 1 }}, paragraph {{ obj[1].paragraph + 1 }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-btn icon>
<v-icon @click="deleteBookmark(obj[0])" title="Remove bookmark"
>mdi-delete</v-icon
>
</v-btn>
</v-list-item-action>
</v-list-item>
</v-list>
</v-card>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
computed: {
...mapState(['bookmarks'])
},
methods: {
...mapActions(['deleteBookmark']),
goTo(obj) {
const [authorName, title] = obj[0].split('/');
this.$router.push({
name: 'showText',
params: {
authorName,
title
},
query: { part: obj[1].part, paragraph: obj[1].paragraph }
});
}
}
};
</script>
store:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
import apiService from '@/services/ApiService';
const store = new Vuex.Store({
state: {
bookmarks: {}
},
mutations: {
SET_BOOKMARKS(state, bookmarks) {
state.bookmarks = bookmarks;
}
},
actions: {
async deleteBookmark({ commit, state }, key) {
let { bookmarks } = state;
const response = await apiService.deleteBookmark(key);
delete bookmarks[key];
commit('SET_BOOKMARKS', bookmarks);
return response;
}
}
});
export default store;
apiService:
import axios from 'axios';
const apiClient = axios.create({
baseURL: process.env.VUE_APP_API_URL,
withCredentials: true,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
responseType: 'json'
});
export default {
deleteBookmark(key) {
return apiClient.delete(`/api/bookmarks/${key}`);
}
};
Red flag right here:
delete bookmarks[key];
Please read Change Detection Caveats.
Use Vue.delete
instead:
Vue.delete(bookmarks, key);
Doing commit('SET_BOOKMARKS', bookmarks);
immediately after doesn't result in any change happening because you're just assigning the same object instance. It might be best to write a REMOVE_BOOKMARK
mutation to handle this so you're not changing the Vuex state outside of a mutation.