1
votes

I have setup a Vuex store and router in Vue. Depending on a piece of state within this store, I would like the router to render a different component for the same path. Below is my attempt at doing this.

Within my router, I include a conditional that is saying "if the user is logged in, return Home, otherwise return Login".

Within my Login component, I change the value of my loggedIn state (within the Vuex store) to true when the user successfully logs in. I have tested and can see this state is being updated, but the Home component is not being rendered upon this state change - it remains as Login.


Login.vue

<template>
  <div className = "container">
    <div className = "sign-in">
        <div className = "inner-sign-in">
            <div className = "title-outer">
                <div className = "title-inner">
                    <h1 className = "reg-title title">Tweeter</h1>
                    <span className = "slogan">Twitter, but not as good.</span>
                </div>
            </div>
            <div className = "form-outer">
                <form className = "sign-in-form" v-on:submit="submitForm">
                    <input @input="updateUsername" type="text" id="username" name="username" placeholder="Username" required/><br/>
                    <input @input="updatePassword" type="password" id="password" name="password" placeholder="Password" required/><br/>
                    <input type="submit" value="Login"/>
                    <div className = "wrongCreds">Sorry, incorrect username or password.</div>
                </form>
                <a className = "register-but" to="/register">Create an Account</a>
                <span className = "guestButton">Continue as Guest &gt;</span>
            </div>
        </div>
    </div>
  </div>
</template>

<script>
  import loginService from '../services/login.js'
  export default {
    name: 'Login',
    computed: {
      loggedIn() {
        return this.$store.getters.loggedIn
      }
    },
    methods: {
      updateUsername: function(event) {
        this.$store.commit('change', {
          name: "username",
          value: event.target.value
        })
      },
      updatePassword: function(event) {
        this.$store.commit('change', {
          name: "password",
          value: event.target.value
        })
      },
      async submitForm(e) {
        e.preventDefault()
        await loginService.login(this.$store.getters.username, this.$store.getters.password)
          .then(data => {
            console.log(data)
            this.$store.commit('change', {
              name: "loggedIn",
              value: true
            })
            console.log(this.$store.getters.loggedIn)
          })
          .catch(error => {
            console.log(error)
          })
      }
    }
  }
</script>

router.js

import { createRouter, createWebHistory } from 'vue-router'
import store from "../store/store"
import Home from "../Home.vue"
import Login from "../Login.vue"

const router = createRouter({
    history: createWebHistory(),
    routes: [
        { path: '/', component: function () {
            console.log(store.getters.loggedIn)
            if(store.getters.loggedIn) {
                return Home
            }
            else {
                return Login
            }
        }}
    ]
})

export default router

store.js

import { createStore } from 'vuex';

const store = createStore({
    state: {
        username: '',
        password: '',
        loggedIn: false
    },
    mutations: {
        change(state, theChange) {
            state[theChange.name] = theChange.value
        },
        arrayItemChange(state, theChange) {
            state[theChange.name][theChange.index] = theChange.value
        }
    },
    getters: {
        username: state => state.username,
        password: state => state.password,
        loggedIn: state => state.loggedIn
    }
})

export default store
3

3 Answers

1
votes

The best practice and what you should do here is to use Navigation Guards.

Now, to speed you up, you need to set 2 navigation guards which checks if user is authenticated.

index.js (router file)

const ifNotAuthenticated = (to, from, next) => {
    if (!store.getters.loggedIn) {
        next();
        return;
    }
    next("/");
};

const ifAuthenticated = (to, from, next) => {
    if (store.getters.loggedIn) {
        next();
        return;
    }
    next("/login");
};

const routes =[
    {
        path: "/home",
        name: "Home",
        component: Home,
        beforeEnter: ifAuthenticated,
      },
    {
        path: "/login",
        name: "Login",
        component: Login,
        beforeEnter: ifNotAuthenticated,
      }
]

beforeEnter here is the key to know whether an authenticated user should proceed to the next page or go to login.

0
votes

Configure the routes to simply have /home and /login (don't make logic there) and handle the login in your template or Vuex: router.push() to the correct path, to match your routes.

0
votes

One of the ways to go about this is to have a parent component that houses Login and Home component, then use Dynamic component to toggle base on the state.

You can check Vue doc here