In my application I need some data to be loaded inside the VueX store before routing starts (for example user sessions).
An example of a race condition would be the following:
// In routes definition { name: 'login', path: '/login', component: Login, meta: { goToIndexIf: () => store.getters['auth/loggedIn'] } }
In this situation the route guard might be executed before the user had been received from the server.
Using conditional rendering did not help as the route guards are executed with or without a <router-view v-if="storeReady">
within the rendered template.
How can I make all my routing wait on some asynchronous data?
Advertisement
Answer
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.