Categories
javascript vue.js vuejs2 vuex

How to deal with async data retrieval with Vuex / Vue

I have a simple app with a common stack :

  • A backend server (Rails)
  • A frontend app (Vue)
  • A database (PG)

The Vue app fetch data from the backend using an action of the Vuex store library as so :

// store/store.js
import Vue from 'vue';
import Vuex from 'vuex';
import * as MutationTypes from '@/store/mutation-types';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
investment: {},
},
mutations: {
[MutationTypes.SET_INVESTMENT_SHOW](state, investment) {
state.investment = investment;
},
},
actions: {
fetchInvestment({ commit }, id) {
InvestmentsApi.get(id).then((response) => {
commit(MutationTypes.SET_INVESTMENT_SHOW, response.data);
});
},
},
getters: {
participation: state =>
state.investment.included[0],
},
});

The action is called in the created lifecycle hook of my component as so :

// components/Investment.vue
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'Investment',
computed: {
...mapState(['investment']),
...mapGetters(['participation']),
},
created() {
this.fetchData(this.$route.params.id);
},
methods: mapActions({
fetchData: 'fetchInvestment',
}),
};

There is a problem in the code I’ve written above, I actually use the computed value ‘participation’ in my template like this :

<BaseTitleGroup
:subtitle="participation.attributes.name"
title="Investissements"
/>

And of course, because I use participation at the time the component renders itself, I get this error from the getter method :

Error in render: "TypeError: Cannot read property '0' of undefined"
found in
---> <InvestmentSummary> at src/views/InvestmentSummary.vue
<App> at src/App.vue
<Root>

I think there are several solutions to solve this problem and I’m wondering which one is best practice or if there is a better one.

  1. The first solution would be to put a v-if attribute in my template to prevent the element from rendering while waiting for the data

    • Con : The rendering offset (the element starts rendering when the data is there) ?
    • Con : I would have to do it for every single component of my app that deals with async data, intuitively I would prefer to deal with this somewhere else (maybe the store?).
  2. Render the element and put fake data in the store, like “Loading…”

    • Con : The little glitch the user sees when loading his page is ugly, when the text switches from loading to the real text.
    • Con : The empty version of my store would be painful to write and super big when my app scales
  3. Change the getter to return the initial empty data, and not the store

    • Con : Getters get more complicated
    • Con : What about data that do not need a getter (maybe they are directly accessible from the state)
  4. Something else ?

I’m looking for the best solution to deal with this pattern, even if it’s one of the above, I’m just not sure which one would be the best. Thanks a lot for reading ! Also, I use vue framework but I think it is more of a general question about modern javascript framework handling of async data and rendering.

Sorry for the long post, here is a potato ! (Oops, not on 9gag 😉 )