Vue.js: Data is not updating with state change so the re-render does not happen
If the computed property isn't referenced (e.g. "used") somewhere in your template code vue will skip reactivity for it.
First it's a bit confusing the way you're structuring the store and the state properties.
I would:
1) Have a "videos" property in the store state
2) Initialise it as an empty array
3) On application start populate it correctly with the "load" defaults, with a mutation that pushes the "default" video to it
4) Have the components mapGetter
to it under the name of videos
5) Whenever you load a component that "updates" the possible videos, then dispatch the action and call the appropriate mutation to substitute the store "videos" property
Note: If components can have different "default" videos, then probably you'll want to have a videos
property in the store that is initialised as false
. This then allows you to have a computed property that uses the getter for the videos
property in the store and in case it is false
What I mean is, for the first case
// store
state: {
videos: []
}
getters: {
videos(state) { return state.videos }
}
//components
...
computed: {
videos() {
this.$store.getters.videos
}
}
For the second case
// store
state: {
videos: false
}
getters: { personal_videos(state) { return state.videos } }
//components
data() { return { default: default_videos } },
computed: {
...mapGetters([ 'personal_videos' ]),
videos() {
if (this.personal_videos) {
return this.personal_videos
} else {
return this.default
}
}
}
Personal: give them better names -_- and the first option is the clearest
So turns out the whole issue was with video.js. I'm half tempted to delete this question, but I would not want anyone who helped to lose any points.
The solution and input here did help to rethink my use of watchers or how I was attempting this. I ended up just using the central store for now since it works fine, but this will need to be refactored later.
I had to just forego using video.js as a player for now, and a regular html5 video player works without issues.
NOTE: at the bottom of this answer, see the general point I make about update/reactivity issues with Vue.
Now, about the question, based on the code you posted, considering the template:
<div v-for="video in videos" :key="video.id">
It picks the videos
from:
data () {
return {
videos: freeVideo
}
}
Although it initializes from freeVideo
, in nowhere in your code you show an update of videos
.
Solution:
You already have the state mapped in the getFreeVideo
computed:
computed: {
...mapState(['getFreeVideo'])
}
Use it:
<div v-for="video in getFreeVideo" :key="video.id">
Update:
I'm setting
videos
in data() to getFreeVideo in the store within the created() lifecycle:this.videos = this.getFreeVideo
This is not enough to keep this.videos
updated with whatever this.getFreeVideo
is. Whenever something is set to this.getFreeVideo
it will only change this.getFreeVideo
, not this.videos
.
If you want to automatically update this.videos
whenever this.getFreeVideo
changes, create a watcher:
watch: {
getFreeVideo() {
this.videos = this.getFreeVideo
}
}
And then keep using videos
in the v-for
:
<div v-for="video in videos" :key="video.id">
Vue's reactivity
If your state is not getting updated in the view, perhaps you are not exploring Vue at its best:
To have Vue automatically react to value changes, the objects must be initially declared in
data
. Or, if not, they must be added usingVue.set()
.
See the comments in the demo below. Or open the same demo in a JSFiddle here.
new Vue({
el: '#app',
data: {
person: {
name: 'Edson'
}
},
methods: {
changeName() {
// because name is declared in data, whenever it
// changes, Vue automatically updates
this.person.name = 'Arantes';
},
changeNickname() {
// because nickname is NOT declared in data, when it
// changes, Vue will NOT automatically update
this.person.nickname = 'Pele';
// although if anything else updates, this change will be seen
},
changeNicknameProperly() {
// when some property is NOT INITIALLY declared in data, the correct way
// to add it is using Vue.set or this.$set
Vue.set(this.person, 'address', '123th avenue.');
// subsequent changes can be done directly now and it will auto update
this.person.address = '345th avenue.';
}
}
})
/* CSS just for the demo, it is not necessary at all! */
span:nth-of-type(1),button:nth-of-type(1) { color: blue; }
span:nth-of-type(2),button:nth-of-type(2) { color: red; }
span:nth-of-type(3),button:nth-of-type(3) { color: green; }
span { font-family: monospace }
<script src="https://unpkg.com/vue"></script>
<div id="app">
<span>person.name: {{ person.name }}</span><br>
<span>person.nickname: {{ person.nickname }}</span><br>
<span>person.address: {{ person.address }}</span><br>
<br>
<button @click="changeName">this.person.name = 'Arantes'; (will auto update because `name` was in `data`)</button><br>
<button @click="changeNickname">this.person.nickname = 'Pele'; (will NOT auto update because `nickname` was not in `data`)</button><br>
<button @click="changeNicknameProperly">Vue.set(this.person, 'address', '99th st.'); (WILL auto update even though `address` was not in `data`)</button>
<br>
<br>
For more info, read the comments in the code. Or check the docs on <b>Reactivity</b> (link below).
</div>
To master this part of Vue, check the Official Docs on Reactivity - Change Detection Caveats. It is a must read!