Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for VueX value to load, before loading component

When a user tries to directly navigate load a component url, an http call is made in my vuex actions, which will define a value in my state once it resolves.

I don't want to load my component until the http call is resolved, and the state value is defined.

For Example, in my component

export default {
  computed: {
    ...mapState({
      // ** this value needs to load before component mounted() runs **
      asyncListValues: state => state.asyncListValues
    })
  },

  mounted () {
    // ** I need asyncListValues to be defined before this runs **
    this.asyncListValues.forEach((val) => { 
      // do stuff
    });
  }
}

How can I make my component wait for asyncListValues to load, before loading my component?

like image 484
Edon Avatar asked Jun 13 '19 19:06

Edon


1 Answers

One way to do it is to store state values.

For example, if your store relies on single API, you would do something like this. However, for multiple APIs, it's a good idea to store each api load state individually, or using a dedicated object for each API.

There are usualy 4 states that you can have, which I prefer to have in a globally accessible module:

// enums.js
export default {
  INIT: 0,
  LOADING: 1,
  ERROR: 2,
  LOADED: 3
};

Then, you can have the variable stored in the vuex state, where the apiState is initialized with INIT. you can also initialize the array with [], but that shouldn't be necessary.

import ENUM from "@/enums";
// store.js
export default new Vuex.Store({
  state: {
    apiState: ENUM.INIT,
    accounts: [],
    // ...other state
  },
  mutations: {
    updateAccounts (state, accounts) {
      state.accounts = accounts;
      state.apiState = ENUM.LOADED;
    },
    setApiState (state, apiState) {
      state.apiState = apiState;
    },
  },
  actions: {
    loadAccounts ({commit) {
      commit('setApiState', ENUM.LOADING);
      someFetchInterface()
        .then(data=>commit('updateAccounts', data))
        .catch(err=>commit('setApiState', ENUM.ERROR))
    }
  }
});

Then, by adding some computed variables, you can toggle which component is shown. The benefit of using state is that you can easily identify the Error state, and show a loading animation when state is not ready.

<template>
  <ChildComponent v-if="apiStateLoaded"/>
  <Loader v-if="apiStateLoading"/>
  <Error v-if="apiStateError"/>
</template>
<script>
import ENUM from "@/enums";
export default {
  computed: {
    ...mapState({
      apiState: state=> state.apiState
    }),
    apiStateLoaded() {
      return this.apiState === ENUM.LOADED;
    },
    apiStateLoading() {
      return this.apiState === ENUM.LOADING || this.apiState === ENUM.INIT;
    },
    apiStateError() {
      return this.apiState === ENUM.ERROR;
    },
  })
}
</script>

aside... I use this pattern to manage my applications as a state machine. While this example utilizes vuex, it can be adapted to use in a component, using Vue.observable (vue2.6+) or ref (vue3).

Alternatively, if you just initialize your asyncListValues in the store with an empty array [], you can avoid errors that expect an array.

like image 75
Daniel Avatar answered Nov 21 '22 01:11

Daniel