Search code examples
vue.jsvuex

Experiencing navbar flicker with Vue.js


I'm experiencing a flicker in my navbar before a function is evaluated to either true or false.

The function that needs to evaluate is the following:

export default {
  methods: {
    isAuthenticated () {
      return this.$store.state.user.authenticated
    }
  },
  data: () => {
    return {
      unauthenticated: [
        {
          title: 'Link1',
          url: '/link1'
        },
        {
          title: 'Link2',
          url: '/link2'
        },
        {
          title: 'Link3',
          url: '/link3'
        }
      ],
      authenticated: [
        {
          title: 'otherLink1',
          url: '/otherlink1'
        },
        {
          title: 'otherLink2',
          url: '/otherlink2'
        },
        {
          title: 'otherLink3',
          url: '/otherlink3'
        }
      ]
    }
  }
}

And the navbar has the following:

<template v-if="isAuthenticated()">
  <b-nav is-nav-bar>
    <b-nav-item v-for="nav in authenticated" :key="nav.title" :href="nav.url">{{nav.title}}</b-nav-item>
  </b-nav>
</template>
<template v-else>
  <b-nav is-nav-bar>
    <b-nav-item v-for="nav in unauthenticated" :key="nav.title" :href="nav.url">{{nav.title}}</b-nav-item>
  </b-nav>
</template>

However, when I click through the navigation, the unauthenticated links appear for a second and then the authenticated links appear as if the isAuthenticated() function hasn't evaluated yet. What can I do to remove this flicker?

My store file (user.js) file looks like this:

export const state = () => ({
  headers: {},
  profile: {}
})

export const mutations = {
  updateHeaders (state, headers) {
    state.headers.access_token = headers['access-token']
    state.headers.token_type = headers['token-type']
    state.headers.client = headers['client']
    state.headers.expiry = headers['expiry']
    state.headers.uid = headers['uid']
    if (state.headers.expiry == null) {
      state.authenticated = false
    } else {
      let timeToExpiry = new Date(state.headers.expiry * 1000)
      let now = new Date()
      state.authenticated = now < timeToExpiry
    }
  },
  signout (state) {
    state.headers = {}
    state.profile = {}
  }
}

The login/logout methods occur via API calls to a Rails app. The Devise gem handles the rest.

Thanks in advance!

EDIT:

I am using Nuxt.js for the layouts/pages/components so I believe that links submit with a this.$router.push(url) under the hood.

The b-nav tags are coming from Bootstrap Vue


Solution

  • When using bootstrap-vue there are two ways to add links to the navbar. One is to bind to :href attribute, which creates a regular html anchor. The other is to use :to attribute, which creates a link that interacts with vue-router.

    <b-navbar-nav v-if="isAuthenticated()">
        <b-nav-item v-for="nav in authenticated" :key="nav.title" :to="nav.url">{{nav.title}}</b-nav-item>
    </b-navbar-nav>
    <b-navbar-nav v-if="!isAuthenticated()">
        <b-nav-item v-for="nav in unauthenticated" :key="nav.title" :to="nav.url">{{nav.title}}</b-nav-item>
    </b-navbar-nav>
    

    No reason to use <template> tags here to encapsulate the . Also note that 'is-nav-bar' is deprecated. See here where they note the deprecation.