I'm trying to get the length of state object from a computed property but it seems to be not reactive.
the state structure:
state: {
user: {
cards: {},
}
}
the getter in my store:
cardCount(state) {
return Object.keys(state.user.cards).length
}
and a computed prop:
calcLeft() {
return this.$store.getters.cardsLeft - this.$store.getters.cardCount
}
on state.user.cards change, i expect from cardCount to return a different value to the computed prop, but that's not happening, it's just stay the same.
Thanks to skirtle, iv'e followed the the usual caveats (vuex),
to change a state prop can be done by the following syntax but it won't be reactive:
state.obj[key] = somevalue
to do that we can use both Vue way or spread syntax to achieve reactivity:
the Vue way:
Vue.set(state.obj, key, value)
or the spread syntax like that:
state.obj = {...state.obj, key: value}
Related
I'm setting an array in my data property through a computed function and it's working. But I wonder how is possible if I don't call it anywhere?
If I try to add a console.log in my function it doesn't print anything, but it's still setting my data, how is that possible?
My data:
data() {
return {
projects: []
};
},
My computed:
computed: {
loadedProjects() {
console.log("Hello there")
this.projects = this.$store.getters.loadedProjects
}
},
I expect that it doesn't run because I'm not calling, and if it is running(I don't know why) to print the console.log before to set my data. Any clarification?
Thanks:)
You're confusing computed props with methods. If you want to have a method like above that sets a data value of your vue instace, you should use a method, not a computed prop:
data() {
return {
projects: []
};
},
methods: {
loadProjects() {
console.log("Hello there")
this.projects = this.$store.getters.loadedProjects
}
}
This would get the value of this.$store.getters.loadedProjects once and assign it to your local projects value. Now since you're using Vuex, you probably want your local reference to stay in sync with updates you do to the store value. This is where computed props come in handy. You actually won't need the projects in data at all. All you need is the computed prop:
computed: {
projects() {
return this.$store.getters.loadedProjects
}
},
Now vue will update your local reference to projects whenever the store updates. Then you can use it just like a normal value in your template. For example
<template>
<div v-for='item in projects' :key='item.uuid'>
{{item.name}}
</div>
</template>
Avoid side effects in your computed properties, e.g. assigning values directly, computed values should always return a value themselves. This could be applying a filter to your existing data e.g.
computed: {
completedProjects() {
return this.$store.getters.loadedProjects.filter(x => x.projectCompleted)
},
projectIds() {
return this.$store.getters.loadedProjects.map(x => x.uuid)
}
}
You get the idea..
More about best practices to bring vuex state to your components here: https://vuex.vuejs.org/guide/state.html
Computed props docs:
https://v2.vuejs.org/v2/guide/computed.html
You should check Vue docs about computed properties and methods
and shouldn't run methods inside computed property getter
https://v2.vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods
Instead of a computed property, we can define the same function as a method. For the end result, the two approaches are indeed exactly the same. However, the difference is that computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when some of its reactive dependencies have changed.
How can I clone data from vuex state to local data attribute?
State
this.tsStore.shemes
Data Attribute
data () {
return { shemes: [] }
}
I've tried do this in updated () this.shemes = this.tsStore.shemes but it's seems like it has a binding left.. because when i delete one item in this.shemes on click i've also delete that item in the state and get the error of "Do not mutate vuex store state outside mutation handlers".
I need to clone the state and do what ever I need to do with that data and on the same time don't affect my state state.
Try
this.shemes = JSON.parse ( JSON.stringify ( this.tsStore.shemes) )
This will clone all value and objects from the array in the store.
You need to create a new array. this.tsStore.shemes give you a reference to the bound array.
You can try to use the spread operator or arr.slice() to create a new array with the same content.
notice that this is a shallow copy.
this.shemes = [...this.tsStore.shemes]
or
this.shemes = this.tsStore.shemes.slice()
Using cloneDeep is still the best way to go, here is an example
<script>
import { cloneDeep } from 'lodash-es'
...
const properlyClonedObject = cloneDeep(myDeeplyNestedObject)
...
</script>
It's bullet proof, battle-tested and is also a tree-shakable function.
If you need this for Nuxt, here is how to achieve this.
data(){
return {
shemes: null,
}
},
beforeMount() {
this.shemes = this.stateShemes
},
computed: {
stateShemes() { return this.tsState.shemes }
// OR that's how I do
stateShemes() { return this.$store.getters['shemes'] }
}
UPDATE
So you get some value from your state by using computed variables. You cannot just assign the value from you store in the data() block. So you should do it beforeMount. That way if you have a watcher for shemes variable, it won't trigger on assigning computed value. If you put it in mounted() hook, the watcher will trigger.
Also, can you explain why do you use this call this.tsState.shemes instead of this.$store.getters.shemes?
In my project , i have a shoppinglists Array to get displayed. When the component is mounted, the store is populated ( it' conatins only one array for the logged customer, fetched from the API db server... wo any problem)
On dissplay, I get the following message :
vue.esm.js?efeb:571 [Vue warn]: Property or method "shoppinglists" is not defined on
the instance but referenced during render. Make sure that this property is reactive,
either in the data option, or for class-based components, by initializing the
property.
See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
The shoppinglists property is defined as computed ...
computed: {
...mapGetters([ { shoppinglists: 'getShoppingLists' } ])
},
And the store contains the shoppinglists array
STATE
{
"shoppinglists":
[{"title":"Groceries","items":[{"text":"Bananas","checked":true},
{"text":"Apples","checked":false}],"id":1,"userId":1}],
"isAuthenticated":true,
"currentUserId":1
}
If I insert a prop declaration in data :
data: function () {
return {
shoppinglists: []
}
},
the warning disappear, but still theres is no list displayed..
what could be wrong ?
thanks for feedback
not exactly duplicated question, but not far from this one
It looks like you have mixed the two different options for mapGetters().
You can either write it like this:
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// mix the getters into computed with object spread operator
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
Which maps this.doneTodosCount to this.$store.doneTodosCount and so on.
Or you could do it this way, which is probably what you want:
...mapGetters({
// map `this.doneCount` to `store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})
And for your example this becomes:
computed: {
...mapGetters({ shoppinglists: 'getShoppingLists' })
},
More documentation and source of the examples are at the bottom of this article.
i'm trying to update a computed propriety using a method inside a component,
here the example:
props: ['selezionati'],
computed:{
listaSelezionati() {
return this.selezionati
}
},
methods:{
rimuoviSotto : function(index,indexparent){
var obj = JSON.stringify(this.listaSelezionati[0][indexparent].sottoservizio[index]);
alert(obj);
var mod = this.listaSelezionati[0][indexparent].sottoservizio.splice(index,1);
vue.set(this.listaSelezionati,mod);
}
}
basically i want to splice a sub array nested data,
the obj var is only to debug, and trigger the correct value, i have tried to apply the array splice without var, it seems to work, but don't apply the modify to component view, so i was trying to use the vue.set but the console return me "is not a function".
basically what can i do to update the computed propriety to the view?
thank you
i solved using $forceUpdate();
basically in the method i must force the computed propriety
computed properties are dependent properties . They update when their dependent data properties which are reactive update.
So assign the vaue of the prop to a data property
props: ['selezionati'],
data((){
return{
listaSelezionati: this.selezionati
}
},
methods:{
rimuoviSotto : function(index,indexparent){
var obj = JSON.stringify(this.listaSelezionati[0][indexparent].sottoservizio[index]);
alert(obj);
var mod = this.listaSelezionati[0][indexparent].sottoservizio.splice(index,1);
this.listaSelezionati = mod;
}
}
If you want to update the prop that you are reviving from the parent , then you should use events as props are one-way data flow
I know that I am supposed to use mutations to change state. However I was wondering if it is theoretivally possible to use state in a v-model binding.
My current solution:
html:
...
<input v-model='todo'>
...
with mutation:
...
computed: {
todo: {
get () { return this.$store.state.todos.todo },
set (value) { this.$store.commit('updateTodo', value) }
}
}
...
without mutation
...
computed: {
todo: {
get () { return this.$store.state.todos.todo },
set (value) { this.$store.state.todos.todo = value }
}
}
...
what I would like:
...
<input v-model='this.$store.state.todos.todo'>
...
You can directly bind a Vuex state property to a component or input via v-model:
<input v-model='$store.state.todos.todo'>
But this is strongly recommended against. Vuex will warn you that you are mutating the state outside of a mutation function.
Since, when using Vuex, your state object is your source of truth which is designed to only be updated in a mutation function, it will quickly become hard to debug why the global state is changing if one component is affecting the global state without calling a mutation.
Most people, I believe, would recommend using your computed todo property example with mutations for the scenario you're describing.