0

I created a Vue.js app with a central store with vuex and some basic API calls with axios to fetch data into the store.

I create the following store action:

  loadConstituencyByAreaCodeAndParliament({commit}, {parliament_id, area_code}) {
    axios.get('/cc-api/area-code/' + parliament_id + '/' + area_code)
         .then((response) => {
           commit('SET_CONSTITUENCY', response.data);
         })
         .catch(function(error){
           commit('SET_CONSTITUENCY', null);
          }
         )
  }

In a single component file I defined a form where the user enters the area code. This form then calls this action to get the constituency fitting the area code:

export default {
  name: 'AreaCodeForm',
  components: {
    PostalCodeInput
  },
  props: ['parliament_id'],
  data: () => ({
    postalCode: ''
  }),
  methods: {
    search_area_code(submitEvent) {
      let area_code = submitEvent.target.elements.area_code.value;
      let payload = {
        parliament_id: this.parliament_id,
        area_code
      }
      this.$store.dispatch('loadConstituencyByAreaCodeAndParliament', payload).
          then(() => {
        let constituency = this.$store.getters.getConstituency();
        // do some things with the data received from the API
        // but everything depending on constituency does not work the first time.
        // Data received from the API is here available only from the second time on
        // wehen this code run.
      })
    }
  }
}

As I found out the $store.dispatch method returns a promise but still the constituency variable receives not the data fetched with the loadConstituencyByAreaCodeAndParliament action but remains empty. I thought when I use the promise.then method the data should be already stored in the store but it is not. When I enter the area code a second time everything works well.

2
  • 1
    You forgot to return the axios.get call (right now, it's just a "fire & forget" promise) Commented Dec 12, 2020 at 10:50
  • Try to avoid using the action promise response directly as it violates some of the core paradigm of one-way data flow. The action should set some data in Vuex, the getter changes accordingly, and you simply use that getter in your component. In other words, use mapGetters rather than imperatively setting constituency. Commented Dec 12, 2020 at 11:09

2 Answers 2

1

As mentioned by blex in a comment returning the axios call is the answer:

  loadConstituencyByAreaCodeAndParliament({commit}, {parliament_id, area_code}) {
    return axios.get('/cc-api/area-code/' + parliament_id + '/' + area_code)
         .then((response) => {
           commit('SET_CONSTITUENCY', response.data);
         })
         .catch(function(error){
           commit('SET_CONSTITUENCY', null);
          }
         )
  }
Sign up to request clarification or add additional context in comments.

Comments

1

Always remember the return statement when dealing with asyncronous tasks. You have two options to refactorize your code, keeping promise or async/await.

Option 1: async/await


async loadConstituencyByAreaCodeAndParliament({ commit }, { parliament_id, area_code }) {
    try {
      const { data } = await axios('/cc-api/area-code/' + parliament_id + '/' + area_code)
      commit('SET_CONSTITUENCY', data)
      return data
    } catch (error) {
      commit('SET_CONSTITUENCY', null)
      return error
    }
  }

Notes:

  • return statement in both blocks of try/catch.
  • .get in axios is optional, since default is get method.
  • You can use object Destructuring assignment with { data } by default with axios. If I'm not wrong the default good http responses retrieve data. Even a more sophisticated way could be const { data: constituencyResponse } = await... then you work with constituencyResponse and you probably save 2 or 3 lines of code each time.

Option 2: Promise


First Path: Make everything in the store.

 // actions
loadConstituencyByAreaCodeAndParliament({ commit, dispatch }, { parliament_id, area_code }) {
  axios('/cc-api/area-code/' + parliament_id + '/' + area_code)
    .then(({data}) => {
      commit('SET_CONSTITUENCY', data)
      dispatch('actionTwo', constituency)
    })
    .catch((error) => {
      console.log("error", error)
      commit('SET_CONSTITUENCY', null)
    })
}

actionTwo({commit}, constituency) {
  console.log("actionTwo", constituency)
  // do something
  commit('COMMIT', 'Final value')
}
// Component
// You handle it with a computed property either referencing a getter or the store state.

{
  computed: {
    getConstituency(){
      return this.$store.state.constituency
    },
    getSomeOtherConstituency(){
      return this.$store.state.constituency.something / 3
    }
  },

  // Optionally if you want to listen and react to changes use a `watcher`.
  watch: {
    // Gets excecuted each time getConstituency updates.
    // ! Must have the same name.
    getConstituency(update) {
      // Do something, `update` is the new value.
    }
  }
}

Second Path: Handle data inside the component, then update the store.

Vue component.

methods: {
 search_area_code(submitEvent) {
    const parliament_id = this.parliament_id
    const area_code = submitEvent.target.elements.area_code.value

    axios('/cc-api/area-code/' + parliament_id + '/' + area_code)
      .then(({data: constituency}) => {
          this.$store.commit('SET_CONSTITUENCY', constituency)
          // Do whatever you want with constituency now inside the component.
        })
      .catch((error) => {
        console.log("error", error)
        this.$store.commit('SET_CONSTITUENCY', null)
      })
  }
},

Notes:

$store.dispatch method returns a promise but still the constituency variable receives not the data fetched with the loadConstituencyByAreaCodeAndParliament action but remains empty. When I enter the area code a second time everything works well.

I think the problem here is that you either handled bad the asyncronous code or trying to implement a custom pattern to work around.

As I said earlier put store getters in computed properties, Look at this example in the Vuex-docs.

Code insights:

// Your action doesn't return anything, you must `return axios.get` inside it.
this.$store.dispatch('loadConstituencyByAreaCodeAndParliament', payload).then(() => {
  let constituency = this.$store.getters.getConstituency()
})

// without the `return` statement the code above can be translated to
this.$store.dispatch('loadConstituencyByAreaCodeAndParliament', payload)
let constituency = this.$store.getters.getConstituency()

// If you opt for async a valid way would be
async doSomething(){
  await this.$store.dispatch('loadConstituencyByAreaCodeAndParliament', payload)
  let constituency = this.$store.getters.getConstituency()
}

// IF it still doesnt update anything try `$nextTick` https://vuejs.org/v2/api/

this.$nextTick(() => {
  this.data = this.$store.getters.getConstituency()     
})

I hope some of this has been helpful.

Comments

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.