Vuex component doesn't render on the page - vue.js

I've got a vue component that make an axios call to get the data and I'm using a Vuex action to make the api call. The problem is that when I load the page the component sometimes render and other times it doesn't. And I don't know what trigger this behaviour.
I'm new to Vuex, any suggestion? thanks in advance
this is the action
getTickets({commit}, apiroute){
console.log('getTickets action')
let config = authHeader();
axios
.get(apiroute,config)
.then(t => t.data)
.then(t => {
commit('setTickets', t);
console.log(t)
})
.catch(error => {
console.log(error.t);
});
}

It is hard to say just based on this information.
Can it be that sometimes Axios call returns before components mounts and that is why it renders the component, while other times component crashes because it is missing some data that it is waiting from this API call?
If that is the case, use the if statement to display some loader until the Axios return and then display the necessary component.

Related

How to render a page content with dynamic routing on page reload?

I have a SPA with dynamic routing. On page loading I'm making a fetch request for products. Then set it to the vuex store and use it. I also have a dynamic routing for product pages '/product/:id'
The problem is if I reload a product page say site.com/product/2 then nothing renders. I think this happens because at that moment store is empty and there're nothing to render.
How can I fix that?
In ProductPage component I tried using navigation guard with no success. Code below returns error: Cannot read properties of undefined (reading 'products').
Fetch request is made on mounting in TheMain component (when I reload ProductPage fetch request is made as well).
ProductPage component:
computed: {
...mapState(["products"]), // get products array from store
},
beforeRouteEnter(to, from, next) {
if (this.products) { // check if products array exists and then load data
next(true);
}
},
I think you need to make beforeRouteEnter async and add await for the store action which does the fetch or axios call.
async beforeRouteEnter(to, from, next) {
if (this.products) {
// check if products array exists and then load data
await dispatch(‘action_to_get_data’);
/// Wait to go to next for dispatch to finish.
next(true);
}
}
In your action you also need to use async / await.
What also might help is a v-if on the ProductPage component, so the components only loads when the products are loaded in the store.
For vue3 you may also take a look at the experimental Suspense component. But I don’t know if this works with beforeRouteEnter.
Hope this helps.

Load async data into the vuex store when Nuxt app loads

I am trialling a project in Nuxt. Liking it so far except I am a little confused as to how to load data from an external async service so that it is available in Vuex from the very first route.
I have tried adding middleware on the default layout to dispatch the store action but I do not see the service being called straight away. Only when I navigate deeper into the routes do I see the action dispatched.
I did something similar in a standard Vue project and added the created method to the App.vue.
Is there a similar way in Nuxt?
What you need is called a fetch.
The fetch method, if set, is called every time before loading the component (only for page components). It will be called server-side once (on the first request to the Nuxt app) and client-side when navigating to further routes.
Warning: You don't have access of the component instance through this inside fetch because it is called before initiating the component.
async fetch({ store }) {
await store.dispatch('your-action')
}
If you need parameter:
async fetch({ store, params }) {
await store.dispatch('your-action', params.id)
}
I gave an example of id. The name of the parameter depends on the name of your page.
_id => params.id
_slug => parmas.slug
...

Recommended way of waiting on an Apollo query before rendering the next page?

When using the Apollo module in a Nuxt app, the default behavior when changing routes is to render the new page immediately, before data has been fetched via Apollo.
This results in some pretty janky rendering experiences where the page does a partial render and very soon after completes rendering with data from the server, making everything on the page shift due to the changing size of components that now have data. This looks pretty bad because the data actually comes back fairly quickly, so it would be fine to wait for the data to return before rendering the new route.
What's the recommended way of waiting on the Apollo queries on a page (and its subcomponents) to complete before rendering the page?
(There's a related question that's not specific to Nuxt, but I'm not sure how to translate the recommendation to a Nuxt app.)
I'd love to see a code example of using beforeRouteEnter to fetch data via Apollo and only entering the route once the data is fetched.
Haven't used this module before, but it should be like any other async action you want to perform before page rendering in Nuxt.
It only depends if you want to pre-fill the store:
https://github.com/nuxt-community/apollo-module#nuxtserverinit
https://nuxtjs.org/guide/vuex-store/#the-nuxtserverinit-action
or only one page:
https://github.com/nuxt-community/apollo-module#asyncdatafetch-method-of-page-component
https://nuxtjs.org/guide/async-data
You can use async/await or promises if you have more than one request before page should be rendered.
When async actions are finished, Nuxt starts rendering the page. This works for SSR and if you navigate to pages on the client (nuxtServerInit will only fire once when real request is made, not when navigating on client side).
Side note: beforeRouteEnter is usually used, to validate params and check if the route is allowed.
did you try disabling the prefetch?
prefetch: false
The best approach is to use the loading attribute:
<template>
<div v-if="!this.$apollo.loading">
Your product: {{product}}
</div>
</template>
<script>
export default {
name: "Product",
apollo: {
product: {
query: productQuery,
variables() {
return {
productId: this.productId
}
}
}
}
}
</script>
I'm unfamiliar with Apollo, but I think this is what you are looking for:
// Router.js
beforeRouteEnter(to, from, next)
{
executeSomeApolloPromise().then((data) => {
// The promise has now been complete; continue to the component.
next((vm) => {
// You have access here to the component instance via `vm`.
// Note that `beforeRouteEnter` is the only guard that has this.
vm.someApolloData = data;
});
});
}
See https://router.vuejs.org/guide/advanced/navigation-guards.html#per-route-guard

Share code in asyncData and mounted using Nuxt

I want to share same code block to get data in asyncData and mounted using Nuxt.
For example in asyncData
async asyncData({ $api, $auth, route, error, store }) {
if( !process.server ) return null;
let res = {};
let stockData = await $api.stocks.getStock(route.params.name);
if( stockData ) { res.stockData = stockData.data; }
return res;
},
And in mounted()
if(ObjectHelper().isEmpty( this.stockData )) {
this.$api.stocks.getStock(route.params.name).then(res => {
this.stockData = res.data;
})
}
Those two code blocks are all getting data from server-side. How to write a common function to reuse it but not write twice? In nuxt documents, You do NOT have access of the component instance through this inside asyncData because it is called before initiating the component.
Regarding your comment to have the API call server and client-side, which is the reason for your question - it is not necessary to duplicate or share it within mounted as asyncDatagets called server and client-side. You will find following in the nuxt docs:
asyncData is called every time before loading the page component. It
will be called server-side once (on the first request to the Nuxt app)
and client-side when navigating to further routes.
That means - lets say you have your asyncData on page A and a user enters your site using page B and navigates client-side via a nuxt-link from page B to page A it will fire asyncData client-side too before initializing the page component.

vuejs component created wait for data to load first

I have an App.vue component where I want to load the currently logged in user. I also want to redirect the user when he\she tries to go the login route and the currently logged in user is already saved in context (he's already signed in).
// inside App.vue component
created() {
AuthService.getCurrentUser().then((user) => {
this.user = user;
});
}
I have a check in the created method of the login component for whether the currentUser is setted, but then when the user tries to go to the login page it might be possible that the the request for the current user is not finished.
My question is:
How do I wait for the data to load before the App.vue component loads?
I saw something like this:
waitForData: true,
data(transition) {
return AuthService.getCurrentUser().then(currentUser => {
transition.next({ currentUser });
});
}
which doesn't actually wait for the data to be loaded and the component loads anyway.
Edit: I'm aware of beforeRouteEnter but this is App.vue component which is a parent component of all components and not a route specific component.
I ran into a very similar problem and solved by adding a v-if on the element that wrapped the child component that depends on the loaded data. And then of course have an data property to control that v-if. If you don't have a wrapping element you can always use a template element to do the wrapping.
This is not the Vue way of doing things.
Use VUEX to store your currentUser object
Set up the Vuex getters in App.vue's computed section. Your template will be updated dynamically once the data is ready.
See the mapGetters section in this page. It works very well with the computed mechanism.
You can also use v-if in your relevant component, so that the component won't be created before your data is ready in VUEX.
If using vue-router, you can use the beforeRouteEnter guard to load data async, as described here: https://router.vuejs.org/en/advanced/data-fetching.html
One way I can think of is when the user is loaed in the App.vue component to check the current path and if it's \login to redirect.
Something like this:
created() {
AuthService.getCurrentUser()
.then(user => this.setCurrentUser(user))
.then(() => {
const { path } = this.$router.currentRoute;
if (path === '/login') {
this.$router.push('/');
}
});