Reputation:
I'm seeing an issue with binded components set from Vuex state management.
I have a state store as follows:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
id: 0,
contentBlocks: []
},
mutations: {
addContentBlock(state, contentBlock) {
contentBlock.id = state.id
state.contentBlocks.push(contentBlock)
state.id += 1
},
updateContentBlock(state, contentBlock) {
const index = state.contentBlocks.findIndex(block => block.id === contentBlock.id)
Vue.set(state.contentBlocks, index, contentBlock)
},
removeContentBlock(state, contentBlock) {
const index = state.contentBlocks.findIndex(block => block.id === contentBlock.id)
Vue.delete(state.contentBlocks, index)
}
}
})
I then loop over state.contentBlocks
in my baseComponent.vue
as follows:
<v-container pa-0 v-for="(contentBlock, index) in contentBlocks" v-bind:key="contentBlock.index">
<component v-bind:is="contentBlock.blockComponent" v-bind:contentBlock="contentBlock"></component>
</v-container>
I then mutate state.contentBlocks
by adding, updating and deleting content blocks. (I add blocks from the base component, deleting and updating from the child component that is bound to <component></component>
.
I get
the mutated state.contentBlocks
to BaseComponent.vue
using within BaseComponent.vue
:
computed: {
contentBlocks: () => store.state.contentBlocks,
}
When updating I see the following in console:
All is looking good. I then go to delete the block at index 1, which "works" on the state level:
But the DOM is not playing ball:
(!!) In the above, the indices of 0 and 2 are correct as the need to be, but the content in index 2 is that of the deleted index 1 (!!?) Help! :D
Upvotes: 2
Views: 72
Reputation: 18187
In baseComponent.vue
, use a computed property to loop over the content blocks rather than the state of the vuex store:
<v-container pa-0 v-for="(contentBlock, index) in blocks" v-bind:key="contentBlock.index">
<component v-bind:is="contentBlock.blockComponent" v-bind:contentBlock="contentBlock"></component>
</v-container>
computed: {
blocks () {
return this.$store.getters['contentBlocks']
}
}
You'll need to add the corresponding getter to the store as well:
export default new Vuex.Store({
state: {
id: 0,
contentBlocks: []
},
getters: {
contentBlocks: (state) => state.contentBlocks
}
mutations: {
addContentBlock(state, contentBlock) {
contentBlock.id = state.id
state.contentBlocks.push(contentBlock)
state.id += 1
},
updateContentBlock(state, contentBlock) {
const index = state.contentBlocks.findIndex(block => block.id === contentBlock.id)
Vue.set(state.contentBlocks, index, contentBlock)
},
removeContentBlock(state, contentBlock) {
const index = state.contentBlocks.findIndex(block => block.id === contentBlock.id)
Vue.delete(state.contentBlocks, index)
}
}
})
Unrelated, but you can simply the removeContentBlock
mutation to:
state.contentBlocks = state.contentBlocks.filter({id} => id !== contentBlock.id)
update
Add a key on the component
elements:
<v-container pa-0 v-for="(contentBlock, index) in blocks" v-bind:key="contentBlock.index">
<component v-bind:is="contentBlock.blockComponent" v-bind:contentBlock="contentBlock" :key="contentBlock.id"></component>
</v-container>
Upvotes: 1