1
votes

So I'm working on a project where I am using Vuex and vue-router. While logging in, currently, the page refreshes the state and reloads the page after clicking on the login button and again goes to /login route. After it receives the data change from watcher, it redirects user to /home path. I want to achieve a loading without going to /login route when receiving data, since it looks weird.

In my store/index.js file, I have this code :

const state = {
  user: null,
  loading: false
}

const getters = {
  user: state => state.user,
  loading: state => state.loading
}

const actions = {
  me : ({ commit }) => {
    commit('setLoading', true)
      apolloClient
        .query({
          query: GET_CURRENT_USER
        })
        .then(({ data }) => {
          setTimeout(() => {
          commit('setLoading', false)
          // Add user data to state
          commit('setUser', data.me)
          console.log(data)
          }, 500)
        })
        .catch(err => {
          commit('setLoading', false)
          console.log(err)
        })
  },

  login: ({ commit }, payload) => {
      localStorage.setItem('token', '')
      apolloClient
        .mutate({
          mutation: LOGIN,
          variables: payload
    })
    .then(({ data }) => {
        setTimeout(() => {
        localStorage.setItem("token", data.login.auth.token)
        router.go('/')
        VueNotifications.success({ message: 'Welcome !' }) 
        }, 500)       
      })
    .catch(err => {
      VueNotifications.error({ message: 'Error' });
    })
  },

  logout : async ({ commit }) => {
    Nprogress.start();
      // clear user in state
      commit('clearUser')
      // remove token in localStorage
      localStorage.setItem('token', '')
      // end session
      await apolloClient.resetStore()
      // redirect to home - kick users out of private pages
      router.push('/session/login')
      Nprogress.done();

  }
}

const mutations = {
  setUser: (state, payload) => {
      state.user = payload
    },
  setLoading: (state, payload) => {
      state.loading = payload
  },
  clearUser: state => (state.user = null)
}

Also, Login.vue is like this :

    computed: {
    ...mapGetters(['loading', 'user'])
    },
    created() {
        this.$store.dispatch('me')    
    },
    watch: {
        deep: true,
        immediate:true,
    user(value) {
      if(value) {
        this.$router.push('/')
            } 
    }
  },
  methods: {        
        loginUser() {
      if(this.$refs.form.validate()) {
      this.$store.dispatch("login", {
        email: this.email,
        password: this.password
        }) 
      }
        }
  }

Lastly, main.js is like this that does implementation of vue-router

router.beforeEach((to, from, next) => {
    if (to.matched.some(record => record.meta.requiresAuth)) {
        // this route requires auth, check if logged in
        // if not, redirect to login page.
        if (!store.getters.user) {
            next({
                path: '/session/login',
            })
        } else {
            next()
        }
    } else {
        next() // make sure to always call next()!
    }
})

Any help is appreciated, thanks !

1

1 Answers

1
votes

inside you top component in beforeCreated hook to update user info on page load:

this.$store.dispatch('user/fetch')

inside you user store module:

const getters = {
  ...
  profile: state => state.profile,
  signedIn: state => Boolean(state.token && state.profile)
  ...
}

later in same user module in actions:

async signIn ({ dispatch }, fields = {}) {
  try {
    // requesting user token
    ...

    await dispatch('setToken', response.data['auth_token'])

    // this function can be removed by using "vuex-persistedstate"
    localStorage.setItem('token', response.data['auth_token'])
    await dispatch('fetch') // fetch user profile after sign in

    router.push({ path: config.redirect.afterSignInPath })

    // success message 'Welcome!'
    ...
  } catch (e) {
    // error message 'login failed'
    ...
  }
},

setToken ({ commit }, token) {
  commit(SET_TOKEN, token)     
  // set auth token for all requests (in axios, for example)
  ...
},

// fetch user profile data with token
async fetch ({ commit, dispatch, getters }) {
  dispatch('fetchTokenFromLS')
  let token = getters['token']
  if(!token)  
    return

  try {
    // requesting user profile
    // commiting it to the store
    ...

  } catch (e) {
    VueNotifications.error({ message: 'Error' });
  }
},

// this can be removed by using "vuex-persistedstate"
async fetchTokenFromLS ({ dispatch }) {
  // try to extract token from localStorage
  let token = localStorage.getItem('token')

  if (token)
    await dispatch('setToken', token)
},

paths.signInPath = '/session/login'

paths.afterSignInPath = '/home'

import store from '~/store'    
...

router.beforeEach((to, from, next) => {
  let pageRequiresAuth = to.matched.some(record => record.meta.requiresAuth)
  let nextParams
  let userSignedIn = store.getters['user/signedIn']

  if (pageRequiresAuth) {
    if (!userSignedIn) 
      nextParams = { path: paths.signInPath }
  } else {
    // login page doesn't require auth.
    // don't let the user see login page if it's already signed in.
    if (userSignedIn) 
      nextParams = { path: paths.afterSignInPath }
  }
  next(nextParams)
}

After all changes you don't need user watcher on home page anymore