✅ Vue 2: computed properties are updated after vuex store mutation.
❌ Vue 3: computed properties are not updated after vuex store mutation.
Purpose: Get posts from Firestore. Add to "postsArray". Commit mutation.
Note: The function "getNextPosts" is called from a (working) intersection observer which allows infinite-scrolling.
const postsArray: Array<string> = [];
const vuexStore = createStore({
state: {
posts: []
},
actions: {
// See https://firebase.google.com/docs/firestore/query-data/query-cursors#paginate_a_query
getFirstPosts({ commit }) {
const getFirstPosts = async () => {
const firstPostsQuery = firestore.collection("posts").limit(3);
// Get FIRST posts.
const firstPosts = await firstPostsQuery.get();
// Add to postsArray.
for (const doc of firstPosts.docs) {
postsArray.push(doc.id);
}
// Add to vuex store.
commit("addFirstPostsToVuex", postsArray);
// Set reference.
lastPostDownloaded = firstPosts.docs[2]; // 3rd post.
};
getFirstPosts();
},
getNextPosts({ commit }) {
const getNextPosts = async () => {
const nextPostsQuery = firestore
.collection("posts")
.startAfter(lastPostDownloaded)
.limit(2);
// Get NEXT posts.
const nextPosts = await nextPostsQuery.get();
// Add to postsArray.
for (const doc of nextPosts.docs) {
postsArray.push(doc.id);
}
// Add to vuex store.
commit("addNextPostsToVuex", postsArray);
// Update reference.
lastPostDownloaded = nextPosts.docs[1]; // 2nd post.
};
getNextPosts();
}
},
mutations: {
addFirstPostsToVuex(state, value) {
state.posts = value;
},
addNextPostsToVuex(state, value) {
state.posts = value;
}
}
});
export default ({
computed: {
posts() {
// List rendering.
return vuexStore.state.posts;
}
}
});
<template>
<div id="feed">
<article class="post" v-for="post in posts" v-bind:key="post.id">
<header class="info">
{{ post }}
</header>
</article>
</div>
</template>
This could be fixed if you use mapState function to map store's state to Vue component's state.
In the code above, that would be something like:
import { mapState } from "vuex";
import { defineComponent } from "vue";
export default defineComponent({
computed: {
...mapState(["posts"])
}
});
The v-for should work just fine now.
Reference: https://vuex.vuejs.org/guide/state.html and https://scrimba.com/scrim/c8Pz7BSK?pl=pnyzgAP
There is a slight difference in defining the state inside Vuex between the old and new version.
**In Vuex3 state was just a prop with an Object while in Vuex4 it has return an Object or a function which returns an Object.**
When migrating from V3 to V4 you might at first not notice the difference because the V3 style kind of works in V4 as well. The difference shows when you've got modules and have multiple instances of them. Then the state has to be a function that returns an object to avoid state pollution (as commented by tony19).
Modules:
// Vuex 3.x Module
const moduleVUEX3 = {
export const state = { ... }
}
// Vuex 4.x Module
const moduleVUEX4 = {
export const state = () => ({ ... })
}
Single Store:
// Vuex 3.x
import Vuex from 'vuex'
const store = new Vuex.Store({
state: { ... }
})
// Vuex 4.x
import { createStore } from 'vuex'
const store = createStore({
state() {
return { ... }
}
})
Solution for this question would be:
const vuexStore = createStore({
state: return {
posts: []
}
})
You could try to remove the postsArray, I assume it has something to do with reactivity.
try changing the mutation to
addFirstPostsToVuex(state, docId) {
state.posts.push(docId);
}
and then in the action you do
for (const doc of firstPosts.docs) {
commit("addFirstPostsToVuex",doc.id);
}
I usually use it like this
<script setup lang="ts">
import { computed, reactive,ComputedRef } from "vue";
import { useStore } from "vuex";
interface State {
shadow: ComputedRef<boolean>
}
const store = useStore();
const state = reactive<State>({
shadow: computed(() => store.state.shadow),
)}
</script>
When the status in vuex changes, the page will respond in time, hope it can help you