Vue-Router and Vuex

Hello all!
I want to protect the routes using “beforeEach” hook in which I check the status of Vuex flag “userIsLogined”. When I try to get this flag, I get its initial value, and not the current state. What I’m doing wrong?

store/modules/users.js

import Vue from "vue";
import router from "../../router";

const state = {
  user: {
    isLogined: false,
    id: null,
    login: null,
    name: null,
    role: null,
    accessToken: null,
    refreshToken: null,
  },
  userIsLogined: 'someText',
};

const mutations = {
  authUser(state, payload) {
    state.userIsLogined = true;
    state.user.isLogined = true;
    state.user.id = payload.userId;
    state.user.login = payload.login;
    state.user.name = payload.name;
    state.user.role = payload.role;
    state.user.accessToken = payload.accessToken;
    state.user.refreshToken = payload.refreshToken;
  },
  logout(state) {
    state.userIsLogined = false;
    state.user.isLogined = false;
    state.user.id = null;
    state.user.login = null;
    state.user.name = null;
    state.user.role = null;
    state.user.accessToken = null;
    state.user.refreshToken = null;
  },

};

const actions = {
  loginUser({commit},payload) {
    return new Promise((resolve,reject) => {
      Vue.axios.post('/users/signin',{
        login: payload.login,
        password: payload.password
      })
        .then(
          response => {
           if(response.data.status === 'success') {
              let payload = {
                userId: response.data.userId,
                login: response.data.login,
                name: response.data.name,
                role: response.data.role,
                accessToken: response.data.accessToken,
                refreshToken: response.data.refreshToken,
              };
              commit('authUser',payload);
              localStorage.setItem('userId',payload.userId);
              localStorage.setItem('login',payload.login);
              localStorage.setItem('name',payload.name);
              localStorage.setItem('role',payload.role);
              localStorage.setItem('accessToken',payload.accessToken.token);
              localStorage.setItem('accessTokenExp',payload.accessToken.expiresIn);
              localStorage.setItem('refreshToken',payload.refreshToken.token);
              localStorage.setItem('refreshTokenExp',payload.refreshToken.expiresIn);
              commit('clearError');
              resolve(true);

            } else {
              commit('setError', response.data.message);
              reject(response.data.message);
            }

          }
        )
        .catch(
          error => {
            if(error.response.status === 401) {
              commit('setError', 'Неверные имя пользователя или пароль');
            } else {
              commit('setError', 'Ошибка сервера. Обратитесь к администратору');
            }
          }
        );
    })
  },

  tryAutoLogin({commit}) {
    const token = localStorage.getItem('refreshToken');
    if(!token) {
      return;
    } else {
      const tokenExpDate = new Date(localStorage.getItem('refreshTokenExp'));
      const now = new Date();

      if (now >= tokenExpDate) {
        localStorage.clear();
        commit('logout');
        return;
      } else {
        Vue.axios.post('/users/refresh-token',{
          refreshToken: token,
        })
          .then(
            response => {
              if(response.data.status === 'success') {
                let payload = {
                  userId: response.data.userId,
                  login: response.data.login,
                  name: response.data.name,
                  role: response.data.role,
                  accessToken: response.data.accessToken,
                  refreshToken: response.data.refreshToken,
                };
                commit('authUser',payload);
                localStorage.setItem('userId',payload.userId);
                localStorage.setItem('login',payload.login);
                localStorage.setItem('name',payload.name);
                localStorage.setItem('role',payload.role);
                localStorage.setItem('accessToken',payload.accessToken.token);
                localStorage.setItem('accessTokenExp',payload.accessToken.expiresIn);
                localStorage.setItem('refreshToken',payload.refreshToken.token);
                localStorage.setItem('refreshTokenExp',payload.refreshToken.expiresIn);
                commit('clearError');

                console.log('Refereshed Tokens', payload);

              } else {
                commit('setError', response.data.message);
                reject(response.data.message);
              }

            }
          )
          .catch(e => {
            if (e.response.status === 404) {
              localStorage.clear();
              commit('logout');
              return;
            }
            console.log(e.response);
          })

      }
    }

  },

  logoutUser({commit}) {
    localStorage.clear();
    commit('logout');
    router.replace('/login');
  },
};

const getters = {
  user(state) {
    return state.user;
  },

  userIsLogined(state) {
    return state.userIsLogined;
  }
};

export default {
  state,
  mutations,
  actions,
  getters
}

router.js

import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
import Login from './views/Login.vue';
import {store} from './store';

Vue.use(Router);

const authGuard = (to, from, next) => {
  console.log('State users', store.state.users);
  console.log('State users.userIsLogined',store.state.users.userIsLogined);
  if (store.state.users.userIsLogined) {
    next()
  } else {
    next('/login');
  }
};

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home,
      beforeEnter: authGuard
    },
    {
      path: '/login',
      name: 'login',
      component: Login
    },
  ]
});

In console I’m getting this output:
State users:
{ob: Observer}
user: Object
userIsLogined: true
ob: Observer {value: {…}, dep: Dep, vmCount: 0}
get user: ƒ reactiveGetter()
set user: ƒ reactiveSetter(newVal)
get userIsLogined: ƒ reactiveGetter()
set userIsLogined: ƒ reactiveSetter(newVal)
proto: Object
router.js?41cb:11
State users.userIsLogined: someText

You likely have a timing issue - you go to / before the login has actually finished and vuex has actually updated its state.

Now you might wonder why the first console log looks fine and the second doesn’t, right? The answer is that:

  1. the first one logged the user object, and this object in the console does update when the object in the store is updated a few miliseconds later (it’s just a reference)
  2. The second console.log logs the userIsLogined property, which is just a string. since this has no reference to any object, it will not be updated in the console when vuex updates shortly later.

In cases this expülanation doesn’t make sense to you, you might have to read up on how Javascript treats Objects & arrays vs. primitive values.

And about how to solve this, I can’t really say much - it depends a btit on where and when you call the login action and when you redirect from there etc, so if you can share more info about that part of your app, we might solve it :slight_smile:

Thanks a lot! I’ll try to fix it by myself, but if not, I’ll text you more