13

Issue

Vue 2: computed properties are updated after vuex store mutation.

Vue 3: computed properties are not updated after vuex store mutation.

Vuex 4

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;
    }
  }
});

Computed properties

export default ({
  computed: {
    posts() {
      // List rendering.
      return vuexStore.state.posts;
    }
  }
});

v-for

<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>
7
  • can you share the full code of the component where you have used the computed property Commented Dec 7, 2020 at 6:22
  • Also instead of importing the entire store you can use ...mapState in your computed property Commented Dec 7, 2020 at 6:23
  • 1
    What if you use vuex getters: vuex.vuejs.org/guide/getters.html Commented Dec 7, 2020 at 7:21
  • please share the main.ts code Commented Dec 7, 2020 at 7:44
  • I'm interested to see a proper solution/explenation for this issue as well so I started a bounty. Commented Oct 21, 2021 at 7:42

4 Answers 4

1

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: []
  }
})
Sign up to request clarification or add additional context in comments.

2 Comments

Vuex 4 state can be either Object or Function. And state being an Object instead of a Function isn't absolutely necessary, and it doesn't affect reactivity (demo). That said, if your app uses multiple instances of a module, using a Function is needed so that each instance has their own state.
@tony19 Thanks for pointing it out - I indeed have multiple module instances inside my app.
0

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

Comments

-1

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);
        }

Comments

-2

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

1 Comment

It doesen't work even with mapState.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.