Skip to content

Vuex 4, State is empty in component

I am trying to access subjects store state using this.$store.state.subjects inside my home component however it comes up as an empty array. Using console.log the only place I am able to see the state.subjects populated is if its in the mutation function. Anywhere else the console.log is empty. It seems to me that the state is not persisting from the mutations, but I’m not sure why.

I have tried quite a few stackoverflow answers however, non of them fix the issues or I have no clue what I am reading in the post. I have also left of code from my code blocks to make this post more readable, such as imports or templates.

Store index.js

export default createStore({
    state: {
        subjects: [],
    },
    actions: {
        getSubjects({ commit }) {
            // Manages subjects, allow for display in column or Calendar view
            axiosMain({
                method: "get",
                url: "/study/",
                withCredentials: true,
            })
                .then((response) => {
                    commit("setSubjects", response.data);
                })
        },
    },
    mutations: {
        setSubjects(state, subjectsToSet) {
            state.subjects = subjectsToSet;
            console.log(state.subjects) # Is a populated array
        }
    }
});

Main.js

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import VueGtag from "vue-gtag-next";
import store from "./store";
import "./assets/main.css";

createApp(App)
    .use(router)
    .use(store)
    .use(VueGtag, {
        property: {
            id: "G-E4DPXQ96HB",
        },
    })
    .mount("#app");

Home.vue

<template>
</template>

<script>
export default {
    name: "Home",
    data() {
        return {
            subjects: [],
        };
    },
    mounted() {
        this.callStoreSubjectAction();
        this.setSubjectsToStoreSubject();
    },
    methods: {
        callStoreSubjectAction() {
            this.$store.dispatch("getSubjects");
        },
        setSubjectsToStoreSubject() {
            this.subjects = this.$store.state.subjects;
            console.log(this.$store.state.subjects); # Is an empty array
        },
    },
};
</script>

Answer

In the component, you’re copying the value of this.$store.state.subjects before the axios call has completed. Wait for the promise to resolve first. To do that, you’ll need to first return the promise from the action:

getSubjects({ commit }) {
  return axiosMain({   // returning the promise
    ... 
  }
}

Waiting for the promise:

mounted() {
  this.$store.dispatch("getSubjects").then(r => {
    this.subjects = this.$store.state.subjects;
    console.log(this.$store.state.subjects);
  });
},

Better than this would be to remove subjects from your component data and use a computed instead to sync with the Vuex state:

import { mapState } from 'vuex';
computed: {
  ...mapState(['subjects'])  // creates a computed `this.subjects`
}

Then you would only have to dispatch the action and the component will take care of the rest:

mounted() {
  this.$store.dispatch("getSubjects");
}