Unwanted Vuex store state mutation triggered - vue.js

I have a mixin for my view components that I use to set the metadata for each page. I'm using Vuex to fetch the default metadata in my store. When including the mixin in each component, I add the "metadata" data attribute and I set the value to the defaultmetadata value in my store. However, when I reassign the value of "metadata" in my mixin / component, Vuex complains that I'm trying to do mutations outside of the store. But I'm just changing the value of "metadata" in my mixin. I'm not touching "defaultmetadata" in the store.
Any ideas ?
the mixin :
import store from 'Store/store'
export default {
data: function () {
return {
metadata: store.state.defaultmetadata
}
},
computed: {
getMetaData: function () {
return this.metadata
}
},
metaInfo () {
let res = {
title: this.getMetaData.title,
meta: [
{ name: 'description', content: this.getMetaData.description }
]
}
return res
},
methods: {
updateMetaData: function (key, value) {
let regex = /(<([^>]+)>)/ig
if (this.textNotEmpty(value)) {
this.$set(this.metadata, key, value.replace(regex, ''))
return true
} else {
return false
}
}
},
beforeRouteEnter (to, from, next) {
next(vm => {
})
}
}
This line that causes the mutation on the store :
this.$set(this.metadata, key, value.replace(regex, ''))
Here, I'm setting the local data attribute "metadata". If I comment the line, Vuex stops complaining. Why does this line trigger a mutation of store data ?
[Vue warn]: Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers."

They still point to a same reference, which is known as shallow copy. You can use JSON.parse(JSON.stringify)) to create a deep copy
data: function () {
return {
metadata: JSON.parse(JSON.stringify(store.state.defaultmetadata))
}
},

Related

Vuex store error when updating existing store item

Little confused here...
I am trying to update a nested object in a Vuex state, which seems to work until I try to add another new value.
Here is my mutation
export const mutations = {
add(state, block) {
state.blocks.push({
type: block,
data: {},
style: {}
})
},
modify(state, [i, key, obj]) {
state.blocks[i][key] = Object.assign({}, state.blocks[i][key], obj)
},
remove(state, index) {
state.blocks.splice(index, 1)
console.log(index)
}
}
Actions:
export const actions = {
createBlock(context, type) {
context.commit('add', type);
},
updateBlock(context, payload) {
context.commit('modify', payload);
},
removeBlock(context, index) {
context.commit('remove', index)
}
}
And my dispatch of the action
this.$store.dispatch('updateBlock', [this.index, 'data', this.obj]) // works
this.$store.dispatch('updateBlock', [this.index, 'style', {m: newMargin}]) //throws error
When I update a Block with the type being data, things work, though when I try to add new data to the style object I get an error
[vuex] do not mutate vuex store state outside mutation handlers.
The end goal is to be able to add key/values to the styles object in the block. This will allow me to create dynamic class names.
What the heck am I missing? I feel like it has to do with Object.assign

Nuxt store getter not working, ID given to payload is not an Integer + Error: [vuex] do not mutate vuex store state outside mutation handlers

I am trying to make a product detail page. The detail page is named _id.
When opened the id is replaced with the product id. On opening the page the state is set with data fetched from an api.
After that i am trying to use a computed property that refers to a getter named getProduct() with an id (this.$route.params.id) in the payload.
This is how my _id.vue looks like:
methods: {
...mapActions("products", ["fetchProducts",]),
...mapGetters("products", ["getProduct",]),
},
async mounted() {
this.fetchProducts()
},
computed: {
product() {
return this.getProduct(this.$route.params.id)
}
}
This is how my store file named products.js looks like:
import axios from "axios"
export const state = () => ({
producten: []
})
export const mutations = {
setProducts(state, data) {
state.producten = data
}
}
export const getters = {
getProduct(state, id) {
console.log(id)
return state.producten.filter(product => product.id = id)
}
}
export const actions = {
async fetchProducts({ commit }) {
await axios.get('/api/products')
.then(res => {
var data = res.data
commit('setProducts', data)
})
.catch(err => console.log(err));
}
}
What works is creating the state, but when i try to use the getter something goes wrong.
As you can see i console.log() the id given to it. Which logs the following:
I also get the error: client.js?06a0:103 Error: [vuex] do not mutate vuex store state outside mutation handlers.
Which I'm not doing as far as I know?
**Note: **these errors get logged as much as the length of my state array is.
From the Vuex documentation:
Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. Like computed properties, a getter's result is cached based on its dependencies, and will only re-evaluate when some of its dependencies have changed.
Like computed, getters does not support having arguments.
But there is a way to have "method-style access" to a getter: https://vuex.vuejs.org/guide/getters.html#property-style-access
You can also pass arguments to getters by returning a function. This is particularly useful when you want to query an array in the store:
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
Note that getters accessed via methods will run each time you call them, and the result is not cached.

How pass state vuex on created()

I have been using vuex for a project for a few days now. Now I need to pass the value of a state to the created method.
Below I show the code used.
Store file
state: {
friendstatus: null
},
mutations: {
SET_FRIEND_STATUS: (state,friend) => {
state.friendstatus = data;
},
actions: {
getFriendStatus: ({commit},data) => {
//axios request returns friendstatus
commit('STATE_FRIEND_STATUS',response.data.status)
}
Vue component
computed: {
...mapstate('friend',['friendstatus'])
},
created() {
//Here I need to pass friendstatus. Obviously if I call this.friendstatus it does not work.
this.$store.dispatch('friend/getFriendStatus);// I would like to call the state and not the action
}

How to handle object mutations in Vuex at once

I have an object in vuex:
state () {
return {
generalInfo: {
userName: 'Bla',
firtsName: 'Bla',
lastName: 'Bla'
}
}
}
I'm using this object in a component with the use of a computed property like so:
computed: {
generalInfo: {
get() {
return this.$store.getters['profile/generalInfo']
},
set(newVal) {
this.$store.commit('profile/setProfileInfo', newVal)
}
}
},
As you can see I've made a mutation in my store that handles the mutation of my object:
mutations: {
setProfileInfo(state, obj) {
state.generalInfo.userName = obj.userName
state.generalInfo.firstName = obj.firstName
state.generalInfo.lastName = obj.lastName
}
},
But for some reason vuex still keeps complaining about setting states outside a vuex mutation. When I do this per object item (i.e. I'm setting a computed property on generalInfo.UserName with a corresponding mutation handler) things work just fine but I don't want to do it on every seperate object item...
I'm using vuex4 (next)

Vuex state change on object does not trigger rerender

I have a variable in the vuex store called permissions. And i want my component to trigger a rerender when the getPermissions changes. In the vue devtools i clearly see that the state has changed in the store, but the component stil get the old state from getPermissions. In order for me to see changes, I have to do a refresh. Has it something to do with the way i mutate it? or the fact that it is an object?
It looks like this when populated:
permissions: {
KS1KD933KD: true,
KD9L22F732: false
}
I use this method to do mutations on it and a getter to get it:
const getters = {
getPermissions: state => state.permissions
};
const mutations = {
set_recording_permissions(state, data) {
let newList = state.permissions;
newList[data.key] = data.bool;
Vue.set(state, 'permissions', newList);
}
};
And in the component i use mapGetters to get access to it
computed: {
...mapGetters('agentInfo',['getPermissions'])
}
In order to update the permissions value i use this action (it does require a succesfull api request before updating the value) :
const actions = {
async setRecordingPermissions({ commit }, data) {
let body = {
agentId: data.userName,
callId: data.callId,
allowUseOfRecording: data.allowUseOfRecording
};
try {
await AgentInfoAPI.editRecordingPermissions(body).then(() => {
commit('set_recording_permissions', { key: data.callId, bool: data.allowUseOfRecording });
commit('set_agent_info_message', {
type: 'success',
text: `Endret opptaksrettigheter`
});
});
} catch (error) {
console.log(error);
commit('set_agent_info_message', {
type: 'error',
text: `Request to ${error.response.data.path} failed with ${error.response.status} ${error.response.data.message}`
});
}
}
}
Since the getter only returns state variable you should use mapState, if you want to access it directly.
computed: mapState(['permissions'])
However, you can also use mapGetters, but then in your template, have to use getPermissions and not permissions.
Example template:
<ul id="permissions">
<li v-for="permission in getPermissions">
{{ permission }}
</li>
</ul>
If you have done this it is probably an issue with the object reference. You use Vue.set, but you set the same object reference. You have to create a new object or set the key you want to update directly.
new object
let newList = { ...state.permissions };
Vue.set
Vue.set(state.permission, data.key, data.value);
I don't know what the rest of you code looks like, but you will need to use actions to correctly mutate you store.
For example:
const actions = {
setName({ commit }, name) {
commit('setName', name);
},
}