[Vuejs]-Vuex store variable is used before it is filled in store with data from API

0👍

You ca try to convert beforeCreated hook to method

methods: {
  async getData() {
    await this.$store.dispatch(
      "getEpisodeFromApi",
      this.$route.params.episode_id
    );
  },
}

then in mounted hook await for data:

async mounted() {
  await this.getData()
  const episode = this.$store.state.episode;
  episode.characters.forEach((name) => {
    this.charactersInEpisode.push(
      this.$store.characters.find((character) => character.name === name)[0]
    );
  });
}

0👍

This may be a computed property. It will be updated when the state fills with data from API:

computed: {
    charactersInEpisode(){
        const episode = this.$store.state.episode;
        if (!episode) return []
        return episode.characters.map((name) => {
           return this.$store.state.characters.find((character) => character.name === name)[0]
        });
    }
}

0👍

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
    />
    <link href="https://cdn.jsdelivr.net/npm/vuetify@1.x/dist/vuetify.min.css" rel="stylesheet" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"
    />
  </head>
  <body>
    <div id="app">
      <v-app>
        <v-content>
          <v-container>
            <h2 class="red pa-2 primary white--text">Only Limited resolved questions</h2>
            <section v-for="(item, index) in questions" :key="index">
              <li>{{index}}.- {{item }}</li>
            </section>

            <section>
              <h3>Response question 1</h3>
              <blockquote class="pa-3 secondary white--text">
                This can be done from any lifecycle hook, or a method. For the example, beforeCreate is used.
              </blockquote>
              <pre>
                    {{ $store.state.episode }}
              </pre>
            </section>

            <section>
              <h3>Response question 2</h3>
              <blockquote class="pa-3 secondary white--text">This via getter and computed property</blockquote>
              <div v-for="(item, i) in charactersInEpisode" :key="i">{{ item }}</div>
            </section>
          </v-container>
        </v-content>
      </v-app>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script src="https://unpkg.com/vuex@2.0.0"></script>
    <script src="https://cdn.jsdelivr.net/npm/vuetify@1.x/dist/vuetify.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

    <script>
      // STORE (e.g. store/index.js)
      const storeTest = new Vuex.Store({
        state: {
          episode: null,
        },
        mutations: {
          setEpisode(state, payload) {
            state.episode = payload[0]; // POSSIBLE IMPROVE FROM BACKEND, SENDING OBJECT {} (NOW ARRAY [])
          },
        },
        actions: {
          async getEpisodeFromApi({ commit }, id) {
            const data = {
              id,
            };
            return await axios.get(`https://www.breakingbadapi.com/api/episodes/${data.id}`).then((response) => {
              console.log("response.dat0dfgsdfa", response.data);
              commit("setEpisode", response.data);
            });
          },
        },
        getters: {
          charactersInEpisode: (state) => {
            console.log("EXE GETTER", state);
            if (state.episode) {
              return state.episode.characters;
            } else {
              return [];
            }
          },
        },
      });

      // DATA LOCAL
      const modelCompartido = {
        questions: {
          1: "How can I fill state in store?",
          2: "get the data from store faster than mounted hook runs code?",
        },
      };
      new Vue({
        el: "#app",
        data: modelCompartido,
        store: storeTest,
        mounted() {},
        async beforeCreate() {
          const episode_id = 1; // TODO: use ID for simplex example, next use/replace the params: this.$route.params.episode_id
          await this.$store.dispatch("getEpisodeFromApi", episode_id);
        },
        computed: {
          ...Vuex.mapGetters(["charactersInEpisode"]),
        },
      });
    </script>
  </body>
</html>

I limit myself only to solving the problem (resolved questions), and I eliminate the rest of the code, due to errors due to undefined variables not provided in the question.

Response question 1: How can I fill state in store?

This can be done from any lifecycle hook, or a method. For the example, beforeCreate is used.

 async beforeCreate() {
      const episode_id = 1; // TODO: use ID for simplex example, next use/replace the params: this.$route.params.episode_id
      await this.$store.dispatch("getEpisodeFromApi", episode_id);
    },

Response question 2: get the data from store faster than mounted hook runs code?

This is possible using Nuxt (Vue framework) through asyncData which makes the request before rendering, but if you are only using Vue it is possible to control it through getters and computed property. (among other ways); This last one is the one that I present as a solution (run code).

// store/index.js
  getters: {
      charactersInEpisode: (state) => {
        console.log("EXE GETTER", state);
        if (state.episode) {
          return state.episode.characters;
        } else {
          return [];
        }
      },
    },

// Component.vue
 computed: {
      ...Vuex.mapGetters(["charactersInEpisode"]),
    },

NOTE:

 setEpisode(state, payload) {
        state.episode = payload[0]; // POSSIBLE IMPROVE FROM BACKEND, SENDING OBJECT {} (NOW ARRAY [])
      },

You can run the solution and check the reactivity. I hope it will be useful

Leave a comment