vuex do not mutate vuex store state outside mutation handlers - vue.js

[vuex] do not mutate vuex store state outside mutation handlers.
When trying to edit the fields this message is shown in the terminal.
How do I use the form correctly?
<q-editor v-model="form.email" min-height="5rem" />
-
data() {
return {
form: {
email: null
}
};
},
computed: {
...mapGetters('auth', ['getSeller'])
},
methods: {
...mapActions('auth', ['setSeller'])
},
created() {
this.form.email = this.getSeller.email;
}
--
vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers."
UPDATE:
<template>
<div>
<q-toggle v-model="FormState.email" />
</div>
</template>
<script>
export default {
computed: {
FormState: {
get () {
return this.$store.state.form
},
set (val) {
this.$store.commit('auth/setSeller', val)
}
}
}
}
</script>
shows the same error
Error: [vuex] do not mutate vuex store state outside mutation
handlers.

The issue is that your v-model is operating directly on this.$store.state.form.email but your computed getter only deals with the this.$store.state.form object.
Your computed getter / setter should only work on a single property if used with v-model.
For example
<q-toggle v-model="formEmail" />
computed: {
formEmail: {
get () {
return this.$store.state.form.email
},
set (val) {
// assuming this mutates the form.email state somewhere
this.$store.commit('auth/setSeller', val)
}
}
}

You can try :
<template>
<q-editor
:value="getSeller"
min-height="5rem"
#input="value => setSeller(value)" />
</template>
<script>
export default {
data() {
return {
form: {
email: null
}
};
},
computed: {
...mapGetters('auth', ['getSeller'])
},
methods: {
...mapActions('auth', ['setSeller'])
}
}
</script>

Related

Vue/Vuex: mapState inside computed is not updating

I am trying to make use of mapState and running into issues with reactive data. I have the following inside my Test.vue component
<template>
<div> {{ name }} </div>
</template>
computed: {
...mapState('user', ['age','name]
}
when my state user.name updates outside of the Test.vue component, the new value is not showing inside Test.vue.
so for example, if I have an update via a mutation in my userStore,
[SET_USER_NAME_MUTATION](state, value) {
state.name = value;
},
commit('SET_USER_NAME_MUTATION', "John")
now in my Vuex store when I check chrome DevTools , user { name: "John" } , which is correct
You should mutate state through vuex actions instead of directly calling the mutation.
Try with something like this, assuming your state contains a user object with name property:
Vue component
<template>
<div>
<span>{{ name }}</span>
<button #click="changeName">Change name</button>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'MyComponent',
computed: {
...mapState({
name: state => state.user.name
})
},
methods: {
changeName () {
this.$store.dispatch('changeName', 'John Smith')
}
}
}
</script>
Vuex store
// state
const state = {
user: {
name: null
}
}
// getters
const getters = {
// ...
}
// actions
const actions = {
changeName ({ commit }, payload) {
commit('setName', payload)
}
}
// mutations
const mutations = {
setName (state, payload) {
state.user.name = payload
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
Anyway, it will be very helpful to know your state structure to a better approach as per your specific case

VueX actions/mutations error in Chrome console

I try to store drawer data in VueX to use it on external component.
My console error: [vuex] unknown action type: app/switchDrawer
My VueJS template:
pages/test.vue
<template>
<v-navigation-drawer v-model="drawer" app>
<v-list dense>
...
</v-list>
</v-navigation-drawer>
</template>
<script>
export default {
computed: {
drawer: {
get () {
return this.$store.state.app.drawer
},
set (value) {
console.log(value);
return this.$store.dispatch('app/toggleDrawer', value)
}
}
}
}
</script>
The console.log() function give me lot of lines in loop in console.
I'd like to use too the mapGetters class from VueX instead computed get/set:
computed: mapGetters({
drawer: 'app/drawer'
})
I've an error in console:
[Vue warn]: Computed property "drawer" was assigned to but it has no
setter.
My VueX store:
store/app.js
export const state = () => ({
drawer: true
})
export const getters = {
drawer: state => state.drawer
}
export const mutations = {
TOGGLE_DRAWER: (state) => {
state.drawer = !state.drawer
}
}
export const actions = {
toggleDrawer ({ commit }, value) {
commit('TOGGLE_DRAWER', value)
}
}
IN CASE YOU DON'T WANT TO MAKE A NEW MUTATION AND HANDLE LOCALLY. (which I preferred personally as my store is pretty big already)
Faced similar issue using when using a vue-ui library(vuesax)
Solved it by initializing a new data variable to a computed variable (the one from the store) in created hook
(Why in created hook)
created() {
this.localDrawer = this.drawer
},
data() {
return {
localDrawer: ''
}
},
computed: {
...mapGetters({
drawer: 'drawer'
})
},
watch: {
drawer(newValue, oldValue) {
this.localDrawer = newValue
}
}
Now use localDrawer in the you app.
NOTE: I am watching the drawer variable as well. So that in any case if its value changes it gets reflected.
Found your problem - a computed setter has to have no return statement.
drawer: {
get () {
return this.$store.state.app.drawer
},
set (value) {
this.$store.dispatch('app/toggleDrawer', value)
}
}
Please notice that your action submits a value to the mutation which dosen't take any value. So better add a new mutation that handles said value:
export const mutations = {
SET_DRAWER: (state, value) => {
state.drawer = value
}
}
export const actions = {
toggleDrawer ({ commit }, value) {
commit('SET_DRAWER', value)
}
}

Refer `this` in App.vue cause undefined error

My App.vue file:
<template>
<v-app app color="grey">
<Navbar v-if="isLoggedIn"></Navbar>
<v-content class="mx-4 mb-4">
<router-view></router-view>
</v-content>
</v-app>
</template>
<script>
import Navbar from "./components/Navbar";
export default {
name: "App",
components: { Navbar },
data: () => {
return {
loggedIn: true
};
},
computed: {
isLoggedIn: () => this.data.loggedIn
},
mounted() {
console.log(process.env);
},
created() {}
};
</script>
Loading the app in browser I got the error message :
vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in render: "TypeError: Cannot read property 'data' of undefined"
Screenshot:
Change this:
computed: {
isLoggedIn: () => this.data.loggedIn
}
to
computed: {
isLoggedIn() {
return this.loggedIn;
}
}
Don't use an es6 arrow function or this will lose access to the proper object. Here is a good answer on Stack Overflow about that.
this.data has to be used as this.$data (otherwise it's looking for a data property on the data property), but don't do that either: you actually don't need to reference data at all, and can go directly to the variable
Using a arrow function creates a anonymous function which means this is bound to window and not the vue instance. Using function ensures the value of this is the vue instance. So change the isLoggedIn computed property to:
computed: {
isLoggedIn: function() { return this.data.loggedIn }
}
// or
computed: {
isLoggedIn() { return this.data.loggedIn }
}

State variable triggers error when displaying it in template as it's temporarily null when component mounts

My user state variable is an object having several properties such as first_name. I want to display some of these properties in my component template.
I'm assign my state variable to a computed property which I use in template thus:
<template>
<div>
{{ user.first_name }}
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
...mapState({
user: state => state.dashboard.user
})
},
beforeMount () {
this.$store.dispatch("dashboard/getUser");
}
};
</script>
Although it works, I get the following error in console:
Error in render: "TypeError: Cannot read property 'title' of null"
I suppose it's because user is null for a split second as component mounts, till it receives info that Vue correctly displays in template. How to avoid the error though?
[EDIT] here are the relevant part of the store:
state: {
user: null
},
...
actions: {
async getUser({ commit }) {
let user = await axios.get(`user`).catch(console.error);
commit("SET_USER", user);
return user;
}
},
In your mapped getter you could default to an empty object like
state => state.dashboard.user || {}
That way things like user.first_name would be undefined rather than attempting to look for a property on the value null
Ok. I've rewritten the code.
store.js
state: {
user: ''
},
mutations: {
SET_USER: (state, user) => {
state.user = user
}
},
actions: {
getUser: (context, user) => {
axios.get('url/to/server')
.then(res => {
context.commit('SET_USER', res.data)
})
.catch(error => {
console.log(error)
})
}
}
Now in your root component (App.vue for example)
import {mapActions} from 'vuex'
export default{
...
mounted() {
this.getUser()
},
methods: {
...mapActions(['getUser'])
}
}
In the component, you wish to use the user data
<template>
<div>
{{user.first_name}}
</div>
<template/>
import {mapState} from 'vuex'
export default{
computed: {
...mapState(['user'])
}
}
This will work.
Hope it helps.

Vuex: Mount() and computed() is executed from a same component, my data is not loading on first?

I'm using action to get all products and then using computed method on the same component to display data. Here is the component:
<template v-for="product in allProducts">
<div class="ap-table-content">
{{ product.product_name }}
</div>
<div class="ap-table-content">
{{ product.code }}
</div>
</template>
And the vue to render allProducts is
export default {
computed :{
...mapGetters([
'allProducts'
]),
},
mounted(){
this.$store.dispatch('fetchAllProducts');
},
}
And on my store.js I have set my store like:
state: {
allProducts : {}
},
getters: {
allProducts: state=>{
return state.allProducts;
}
},
actions:{
fetchAllProducts : async({commit, state})=>{
await axios.get('/products')
.then(response => {
commit('storeProducts', response.data);
})
.catch(err => console.log(err));
},
}
SOLUTION:
It was my mutator. I was assigning a lump of my array in the object without getting the state.property where computed lost its value.
storeProducts(state, data){
Object.assign(state.allProducts, data);
}
This mutation was the culprit.
Can someone give me better option to attach data at state?
JavaScript is a case-sensitive language. You're trying to use a getter called allProducts but what you declared in your store getters is allproducts and that's why your data is not displaying.
Modify your getter function to allProducts instead of allproducts.
getters: {
allProducts: state=>{
return state.allProducts;
}
},
Also I noticed that you used store: { allProducts: {} } inside your module instead of state.
It's okay if you are exporting it as state later but if not, then you should change it to state: { allProducts: {} }