pk_45
pk_45

Reputation: 13

Vue-Router Data Fetch: Fetch Data before 'beforeRouteUpdate' loads component

I am new to vue-router navigation guards and so I recently realized that I needed to use beforeRouteUpdate guard for reused components where for example: Going from /foo/1 to /foo/2

However, while coming to /foo/1, I pulled data from database through an axios call and before going to /foo/2, I need to pull new data again through the axios call.

This is where I face a problem where the navigation guard beforeRouteUpdate loads the component /foo/2 before my data loads from the axios call and thus I get null in a few of my variables.

How can I make beforeRouteUpdate wait to load the next component so that all my data is loaded from the axios calls?

As for my code, it looks like this:

beforeRouteUpdate (to, from, next) {
    Vue.set(this.$store.state.user, 'get_user', null)
    this.$store.dispatch(OTHER_PROFILE_GET, to.params.id).then(resp => {
      console.log(resp);
      if(this.$store.getters.is_user_loaded) {

        next()

      } else {

        this.$store.watch((state, getters) => getters.is_user_loaded, () => 
        {
          if(this.$store.getters.is_user_loaded) {
            console.log(this.$store.state.user.get_user);
            console.log('comes here');
            next()
          }
        })
      }
    })
}, 

To explain my code further, I have called this method in my component and so I when I go from /user/1 to /user/2 I dispatch a Vuex action which makes an axios call to get the new profile details but before the axios call completes and loads the data in the Vuex state, the beforeRouteUpdate already loads the next component.

Upvotes: 0

Views: 3503

Answers (1)

Phil
Phil

Reputation: 164980

First, your action should perform any state mutation such as setting user.get_user to null. I'm also not sure why you've added a watch; your action should only resolve when complete. For example

actions: {
  [OTHER_PROFILE_GET] ({ commit }, id) {
    commit('clearUserGetUser') // sets state.user.get_user to null or something
    return axios.get(`/some/other/profile/${encodeURIComponent(id)}`).then(res => {
      commit('setSomeStateData', res.data) // mutate whatever needs to be set
    })
  }
}

then your route guard should have something like

beforeRouteUpdate (to, from, next) {
  this.$store.dispatch(OTHER_PROFILE_GET, to.params.id).then(next)
}      

In order to prevent errors from trying to render null data, use your getters. For example, say your getter is

getters: {
  is_user_loaded (state) {
    return !!state.user.get_user
  }
}

in your component, you can map this to a computed property...

computed: {
  isUserLoaded () {
    return this.$store.getters.is_user_loaded // or use the mapGetters helper
  },
  user () {
    return this.$store.state.user.get_user // or use the mapState helper
  }
}

then in your template, use this logic to conditionally render some data

<div v-if="isUserLoaded">
  Hello {{user}}
</div>
<div v-else>
  Loading...
</div>

This is the suggested approach in the vue-router guide for beforeRouteUpdate

Upvotes: 3

Related Questions