Reputation: 251
let's say I have following tree:
[
{
name: 'asd',
is_whatever: true,
children: [
{
name: 'asd',
is_whatever: false,
children: [],
},
],
},
],
The tree is stored in a module via Vuex under key 'tree' and looped through with following recursive component called 'recursive-item':
<li class="recursive-item" v-for="item in tree">
{{ item.name }}
<div v-if="item.is_whatever">on</div>
<div v-else>off</div>
<ul v-if="tree.children.length">
<recursive-item :tree="item.children"></recursive-item>
</ul>
</li>
Now i want to toggle item's property 'is_whatever', so i attach a listener
<div v-if="item.is_whatever"
@click="item.is_whatever = !item.is_whatever">on</div>
<div v-else>off</div>
When i click it, it works, but emits following
"Error: [vuex] Do not mutate vuex store state outside mutation handlers."
[vuex] Do not mutate vuex store state outside mutation handlers.
How am I supposed to implement it without this error? I can see no way how to dispatch an action or emit event to the top of the tree because it's nested and recursive, so I haven't got a path to the specific item, right?
Upvotes: 10
Views: 5617
Reputation: 61
There is a debatable solution, but I'll just leave it here.
State:
state: {
nestedObject: {
foo: {
bar: 0
}
}
}
There is Vuex mutation:
mutateNestedObject(state, payload) {
const { callback } = payload;
callback(state.nestedObject);
},
And this is an example of use in a component:
this.$store.commit('mutateNestedObject', {
callback: (nestedObject) => {
nestedObject.foo.bar = 1;
},
});
Upvotes: 0
Reputation: 251
After consulting with some other devs later that evening we came with few ways how to achieve it. Because the data are nested in a tree and I access the nodes in recursive manner, I need to either get the path to the specific node, so for example pass the index of a node as a property, then add the child index while repeating that in every node recursively, or pass just the id of a node and then run the recursive loop in the action in order to toggle its properties.
More optimal solution could be flattening the data structure, hence avoiding the need for a recursion. The node would be then accessible directly via an id.
Upvotes: 5
Reputation: 33537
Right now you're changing the state object directly by calling item.is_whatever = !item.is_whatever
, what you need to do is create a mutation function that will execute that operation for you to guarantee proper reactivity:
const store = new Vuex.Store({
state: { /* Your state */ },
mutations: {
changeWhatever (state, item) {
const itemInState = findItemInState(state, item); // You'll need to implement this function
itemInState.is_whatever = !item.is_whatever
}
}
})
Then you need to expose this.$store.commit('changeWhatever', item)
as an action in your view that'll be trigger by the click.
Upvotes: 0