How to prevent any routing before some async data (in Vuex store) has loaded?
Since this question was first asked, vue-router (v3.5.1) has exposed the ability to check for the initial navigation to perform operations like this and run on the first route only.
Compare from
to VueRouter.START_LOCATION
.
import VueRouter from 'vue-router'
const router = new VueRouter({
// ...
})
router.beforeEach((to, from, next) => {
if (from === VueRouter.START_LOCATION) {
// initial navigation, handle Vuex initialization/hydration.
initalizeOrWait().then((isLoggedIn) => {
// handle navigation or pass to guard how it fits your needs here etc.
next();
});
} else {
next();
}
})
The solution is simple. Add an init
or equivalent Vuex action
to the relevant parts of your store.
It should return a Promise
of all the requests for data that your application absolutely needs*:
init ({ dispatch }) { // Could also be async and use await instead of return
return Promise.all([
dispatch('getUserSession'), // Using another action
dispatch('auth/init'), // In another module
fetch('tehKittenz') // With the native fetch API
// ...
])
}
The above code can use anything that returns a Promise
.
Then just create a global navigation guard in your router using beforeEach
.
This guard will wait on the promise generated by a dispatch
to the store.
// In your router initialization code
const storeInit = store.dispatch('init')
// Before all other beforeEach
router.beforeEach((to, from, next) => {
storeInit.then(next)
.catch(e => {
// Handle error
})
})
This way, if routing happens before the store is fully loaded the router will simply wait.
If routing happens after, the promise will already be in a fulfilled
state and routing will proceed.
Don't forget to use something like conditional rendering so as not to display a blank screen while routing is waiting on the data.
*: This will prevent all routing and navigation as long as the data is being fetched. Be careful.