Search code examples
javascriptvuejs3vue-router4

How to prevent authtenticated page flasshing the login page VUEJS 3


I am using vue3 can I ask for some help why is it redirecting to an authenticated page like home, when I refresh the login page will flash. every time I refresh it keeps flashing, how can I prevent this flashing login when I refresh the page or anywhere as long as I'm authenticated.

Thank you in advance.

// main.ts

import { createApp } from 'vue'
import App from './App.vue'
import { store } from '@/vuex/store.js';

import router from './router';

const app = createApp(App)


app.use(store);
app.use(router)
app.mount('#app')

//router

import {createRouter, createWebHistory} from 'vue-router'
import HomeView from '@/views/HomeView.vue'

import Login from '@/components/Login.vue'

// @ts-ignore
import {store} from "@/vuex/store";

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: [
        {
            path: '/login',
            name: 'login',
            component: Login,

        },
        {
            path: '/home',
            name: 'home',
            component: HomeView
        }

    ]
})

router.beforeEach((to, from, next) => {

 next()
})

export default router

// login.vue

<template>
   <div>
   <h1>Login </h1>
      <form action="#" method="post">
          <div class="input-group mb-3">
            <input type="text" id="username" v-model="username" class="form-control" placeholder="Username">
            <div class="input-group-append">
              <div class="input-group-text">
                <span class="fas fa-envelope"></span>
              </div>
            </div>
          </div>
          <div class="input-group mb-3">
            <input type="password" id="password" v-model="password" class="form-control" placeholder="Password">
            <div class="input-group-append">
              <div class="input-group-text">
                <span class="fas fa-lock"></span>
              </div>
            </div>
          </div>
          <div class="row">
            <div class="col-8">
              <div class="icheck-primary">
                <input type="checkbox" id="remember">
                <label for="remember">
                  Remember Me
                </label>
              </div>
            </div>
            <!-- /.col -->
            <div class="col-4">
              <button type="submit" class="btn btn-primary btn-block" @click.prevent="login">Sign In</button>
            </div>
            <!-- /.col -->
          </div>
        </form>
   </div>
</template>

<script setup>
import {ref} from 'vue';
import {useStore} from 'vuex'
import {useRouter} from 'vue-router';


const username = ref('');
const password = ref('');

const store = useStore();
const router = useRouter();


const login = async () => {

  await store.dispatch('fetchToken', {username: username.value, password: password.value});
  await store.dispatch('fetchUserLogin');
  await router.push({name: 'home'});
}


</script>

// App.vue

<template>

  <router-view v-if="store.state.isLogin"/>
  <login v-else></login>

</template>

<script setup lang="ts">
import Login from '@/components/Login.vue'

import {useStore} from 'vuex';
import {RouterView} from "vue-router";
import {onBeforeMount, onMounted, ref} from "vue";

const store = useStore();


onMounted(async ()=>{
  await store.dispatch('fetchUserLogin')
});


</script>

Solution

  • <router-view v-if="store.state.isLogin"/>
    <login v-else></login>
    

    while store.state.isLogin is false, the page will show the Login component. store.state.isLogin is always initially false until this asychronous operation completes:

    await store.dispatch('fetchUserLogin')
    

    That's why you always see a flash of the login component. store.state.isLogin is false until the dispatch is complete, which takes some small amount of time.

    There are multiple ways you could handle this, one would be to have a isLoading property that is initially true and is then set to false once the fetchUserLogin is done. You can use isLoading to choose to not render anything (or render some loading modal) while the fetching is ongoing:

    <div v-if="isLoading"></div>
    <div v-else>
      <router-view v-if="store.state.isLogin"/>
      <login v-else></login>
    </div>
    
    const store = useStore();
    const isLoading = true;
    
    onMounted(async () => {
      await store.dispatch('fetchUserLogin');
      isLoading = false;
    });