Search code examples
laravelvue.jsvuex

Access Vuex State from VueRouter


I am trying to wrap my head around Navigation Guards for vue.js. I tried to follow the example but I think I am falling short in connecting my State (store.js) to my Router (routes.js). In my Login.vue component I try and push to a new route after the loadUser has dispatched to the State and subsequently the isLoggedIn is updated to true. In the vue components I use mapState do I need to do the same thing here in routes.js. Or maybe there is a better way to do this. To give a full picture I am using a vue frontend and laravel backend with Sanctum.

Extract from routes.js

const routes = [
    {
        path: '/',
        component: Login,
        name: 'login'
    },
    {
        path: '/dashboard',
        component: Dashboard,
        name: 'dashboard',
        meta: { requiresAuth: true }
    },
]

const router = new VueRouter({
    mode: 'history',
    routes // short for `routes: routes`
});

router.beforeEach((to, from, next) => {
    // if (to.matched.some(record => record.meta.requiresAuth)) {
    if (to.meta.requiresAuth) {
        if (store.state.isLoggedIn) {
            next();
        } else {
            next({
                name: 'home'
            });
        }
    } else {
        next();
    }
});

export default router;

Extract from store.js

export default {
    state : {
        isLoggedIn: false,
    }
}

Extract from Login.vue

methods : {
        async login() {
            this.loading = true;
            this.errors = null;

            try {
                await axios.get('/sanctum/csrf-cookie');
                await axios.post('/login', this.user);

                logIn();
                this.$store.dispatch('loadUser');
                this.$router.push({ name: 'logbook'});

            } catch (error) {
                this.errors = error.response && error.response.data.errors;
            }

            this.loading = false;
        }
    },

Solution

  • If you want to access/change the Vuex in VueRouter, you must to use the command router.app.store.state....

    For example:

    const routes = [
      { path: '/', component: Login, name: 'login' },
      { path: '/dashboard', component: Dashboard, name: 'dashboard', meta: { requiresAuth: true } },
    ];
    
    const router = new VueRouter({
      mode: 'history',
      routes,
    });
    
    router.beforeEach((to, from, next) => {
      if (router.app.$store) {
        // Example to access the state
        console.log(router.app.$store.state.isLoggedIn);
    
        // Example to exec the mutation
        router.app.$store.commit('myMutation', 'myArgs');
    
        // Example to exec the action (ATTENTION IS ASYNC)
        router.app.$store.dispatch('myAction', 'myArgs');
      }
    
      if (to.meta.requiresAuth) {
        // ...
      } else {
        next();
      }
    });
    
    export default router;
    
    

    Attention: You must load the Vuex first than VueRouter. Because, when the VueRouter load the Vuex need to be ready. You can change this in main.js:

    import Vue from 'vue'
    import App from './App.vue'
    import store from './store' // FIRST IMPORT
    import router from './router' // SECOND IMPORT
    
    Vue.config.productionTip = false
    
    new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount('#app')
    
    

    Other thing, I prefer to control of login in cookies, because the user can open many tabs. It's my opinion, you can to do what you prefer.