Search code examples
javascriptvue.jsvuexvue-routeres6-promise

Use promised getter in VueRouter beforeEach


I have been struggling to use a vuex.getters which is computed from a vuex.actions as an if statement in router.beforeEach.

Here is what I am trying to do:

  1. Fetch an array of objects every time user enter the site.

  2. And then use array.find() to target the one object with the same slug according to the router.params.id in case user enter the site with direct url.

  3. Redirect route to 404 if router.params.id doesn't match any slug in the object array.

Here is my approach

const router = new VueRouter(
  {
    routes: [
      {
        path: '/list/:id',
        name: 'list'
      },
      {
        path: '/edit/:id',
        name: 'edit'
      },
      //other routes
      {
        path: '/*',
        name: '404'
      }
    ]
  }
);
router.beforeEach((to, from, next)=>{

  if ( !store.state.lists.length ){
   // fetch Object Array when user first enter
    store.dispatch('fetchLists');

  }


  if ( to.name === 'list' || to.name === 'edit'){
    // limit route only 'list' & 'edit', preventing passing undefined params
      store.commit('cloneParams', to);

     // determine if currentMonth has a valid slug, if not go to 404
      store.getters.currentMonth.slug !== '' ? next() : next('/404');

  } else {

    next();
  }

});
getters: {
  currentMonth(state){
    var template = {
      month: '',
      slug: '',
      data: []
    }
    var obj = template;

    if ( state.lists.length ) {

      obj = state.lists.find(current => current.slug === state.paramsID);

      if ( obj === undefined ){
        obj = template
      }
    }

    return obj;
  },

actions: {
  fetchLists({commit}){
    axios('/lists').then(
      res=>{
        commit('storeList', res);
      }
    )
  },
mutations: {
  storeList(state, res){
    state.lists = res.data;
  },
  cloneParams(state, to){
    state.paramsID = to.params.id;
  },

However, I found that the getter currentMonth does not update after the object array store.state.lists is fetched and goes to 404. Meanwhile it works perfectly fine after the store.state.lists is fetched and stored when user goes to next other page of this SPA.


Solution

  • The route guard isn't waiting for the http request to complete before checking for the api result. Return the promise from fetchLists:

    actions: {
    fetchLists({commit}){
      return axios('/lists').then(res => {          // return the promise
        commit('storeList', res);
      })
    },
    

    And wait for that promise in the navigation guard:

    router.beforeEach(async (to, from, next) => {   // async keyword
    
      if ( !store.state.lists.length ){
        await store.dispatch('fetchLists');         // Waiting for the promise
      }
    
      if ( to.name === 'list' || to.name === 'edit'){
        // limit route only 'list' & 'edit', preventing passing undefined params
          store.commit('cloneParams', to);
    
         // determine if currentMonth has a valid slug, if not go to 404
          store.getters.currentMonth.slug !== '' ? next() : next('/404');
    
      } else {
        next();
      }
    
    });