How to prevent Vue make API call on back button click and instead take data from Vuex? - vue.js

I'm newbie to Vue 3. I trying to do my first app with Vue 3 + Vuex + Vue route.
Step-by-step:
On page load --> API call --> received data set to Vuex store.
Render items on a page from store.
When user clicks on item on homepage, then a new page loading with the data from store (no second API call).
Propblem: when user clicks on browser back button then homepage is rendered again but it makes an API call for the items but I do not need them as they're in store.
How to fix that so if browser back button clicked then no second API call for the same data?
Main component
methods: {
...mapActions('characters', ['getCharacters']),
},
mounted() {
this.getCharacters();
},
Store action which makes the request:
actions: {
async getCharacters({ state, commit }, page = state.currentPage) {
try {
const res = await fetch(`${BASE_URL}character/?page=${page}`);
if (res.ok) {
const { info, results } = await res.json();
commit('setCharacters', { list: results });
}
} catch (e) {
console.log(e);
}
},
};

You can check if characters data exists before invoke getCharacters()
computed: {
...mapState('characters', ['characters']),
},
methods: {
...mapActions('characters', ['getCharacters']),
},
mounted() {
if (!this.characters) {
this.getCharacters();
}
},

Related

vue/vuex: Can you re-render a page from another page?

With the first login in my app, users get a possibility to leave their address. When this address is stored, the user are pushed to their dashboard. Second login the user go straight to the dashboard.
I have 2 Vuex states that are updated with the response.data. 'Signed' leads to address page, 'Frequent' leads to 'dashboard'.
//PROMPT.VUE
mounted () {
this.getPrompt()
},
computed: {
promptStatus () {
return this.$store.getters.getPrompt
}
},
methods: {
async getPrompt() {
try{
await //GET axios etc
// push prompt status in Store
let value = response.data
this.$store.commit('setPrompt', value)
if (this.promptStatus === 'signed') {
this.$router.push({path: '/adres'})
}
if (this.promptStatus === 'frequent') {
this.$router.push({path: '/dashboard'})
}
When user leaves the address I reset the vuex.state from 'signed' to 'frequent'.
//ADRES.VUE
//store address
let value = 'frequent'
this.$store.commit('setPrompt', value)
this.$router.push({name: 'Prompt'})
The Vuex.store is refreshed. But the Prompt.vue wil not re-render with the new vuex.status. Many articles are written. Can 't find my solution. Maybe I organize my pages the wrong way.
In views, it is not recommended to mutate data (call commit) outside vuex. Actions are created for these purposes (called from the component using dispatch). In your case, you need to call action "getPrompt" from the store, but process routing in the authorization component. This is more about best practice
To solve your problem, you need to make a loader when switching to dashboard. Until the data is received, you do not transfer the user to the dashboard page
Example
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "DashboardLayout",
components: { ..., ... },
data: () => ({
isLoad: false
}),
async created() {
this.isLoad = false;
try {
await this.$store.dispatch('getData');
this.isLoad = true;
} catch (error) {
console.log(error)
}
}
});
</script>
Data is received and stored in the store in the "getData" action.
The referral to the dashboard page takes place after authorization. If authorization is invalid, the router.beforeEach handler (navigation guards) in your router/index.js should redirect back to the login page.
Learn more about layout in vuejs
Learn more about navigation guards

Gridsome mounted method is only running on page reload

I am using the vue mounted lifecyle method to fetch data. The data is stored in algolia. I use the search api to connect and fetch it. The data is only loaded when I refresh the site. It does not run on page navigation.
methods: {
async fetchInventory(data = {}) {
try {
this.isLoading = true;
const result = await index.search("", {hitsPerPage: 12});
this.auctions = result.hits;
this.totalItems = result.nbHits;
this.totalPages = result.nbPages;
this.isLoading = false;
} catch (error) {
this.isLoading = false;
console.log(error);
}
},
},
mounted() {
this.fetchInventory();
}
If this is client side rendering you may need to wait until nextTick OR use earlier/later hook:
mounted() {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
May need to use beforeCreate or created hook if rendering serverside.
Also how is page navigation being done? if you're using navigation API or a library that may be critical to fixing the issue

Vuex: Why is store only updated when dispatching page is refreshed?

I have a vue component that gathers some data that should eventually be used to update a vuex store.
On the click of a button this function is triggered:
confirmResult() {
var type = this.type;
var value = this.max_keys_formatted.toLowerCase();
const updateData = {
type: type,
value: value,
}
this.$store.dispatch("updateType", updateData);
this.$router.replace("/home") //replace: no going back (as against 'push')
},
In my store, the action updateType calls the mutation updateType that updates the state (or at least it should):
const store = createStore({
state() {
return {
sellerType: "initiativ",
buyerType: "dominant",
}
},
mutations: {
updateType(state, updateData) {
console.log("type: ", updateData.type);
console.log("value: ", updateData.value);
if (updateData.type == "seller") {
console.log("now in seller");
state.sellerType = updateData.value;
}
if (updateData.type == "buyer") {
state.buyerType = updateData.value;
}
}
},
actions: {
updateType(context, updateData) {
context.commit("updateType", updateData); // Place to store in backend server
}
},
});
Now, when I refresh the page that contains the intially triggering button, this all works fine and my console outputs:
But when I navigate to the page that contains the intially triggering button, but do not refresh it, the output on the console remains the same, but the store is not updated!
I can't figure out why that is! If you need additional information, please let me know!

Vuex populate data from API call at the start

apologies for the simple question, I'm really new to Vue/Nuxt/Vuex.
I am currently having a vuex store, I wish to be able to populate the list with an API call at the beginning (so that I would be able to access it on all pages of my app directly from the store vs instantiating it within a component).
store.js
export const state = () => ({
list: [],
})
export const mutations = {
set(state, testArray) {
state.list = testArray
}
}
export const getters = {
getArray: state => {
return state.list
},
}
I essentially want to pre-populate state.list so that my components can call the data directly from vuex store. This would look something like that
db.collection("test").doc("test").get().then(doc=> {
let data = doc.data();
let array = data.array; // get array from API call
setListAsArray(); // put the array result into the list
});
I am looking for where to put this code (I assume inside store.js) and how to go about chaining this with the export. Thanks a lot in advance and sorry if it's a simple question.
(Edit) Context:
So why I am looking for this solution was because I used to commit the data (from the API call) to the store inside one of my Vue components - index.vue from my main page. This means that my data was initialized on this component, and if i go straight to another route, my data will not be available there.
This means: http://localhost:3000/ will have the data, if I routed to http://localhost:3000/test it will also have the data, BUT if i directly went straight to http://localhost:3000/test from a new window it will NOT have the data.
EDIT2:
Tried the suggestion with nuxtServerInit
Updated store.js
export const state = () => ({
list: [],
})
export const mutations = {
set(state, dealArray) {
state.list = dealArray
}
}
export const getters = {
allDeals: state => {
return state.list
},
}
export const actions = {
async nuxtServerInit({ commit }, { req }) {
// fetch your backend
const db = require("~/plugins/firebase.js").db;
let doc = await db.collection("test").doc("test").get();
let data = doc.data();
console.log("deals_array: ", data.deals_array); // nothing logged
commit('set', data.deals_array); // doesn't work
commit('deals/set', data.deals_array); // doesn't work
}
}
Tried actions with nuxtServerInit, but when logging store in another component it is an empty array. I tried to log the store in another component (while trying to access it), I got the following:
store.state: {
deals: {
list: []
}
}
I would suggest to either:
calling the fetch method in the default.vue layout or any page
use the nuxtServerInit action inside the store directly
fetch method
You can use the fetch method either in the default.vue layout where it is called every time for each page that is using the layout. Or define the fetch method on separate pages if you want to load specific data for individual pages.
<script>
export default {
data () {
return {}
},
async fetch ({store}) {
// fetch your backend
var list = await $axios.get("http://localhost:8000/list");
store.commit("set", list);
},
}
</script>
You can read more regarding the fetch method in the nuxtjs docs here
use the nuxtServerInit action inside the store directly
In your store.js add a new action:
import axios from 'axios';
actions: {
nuxtServerInit ({ commit }, { req }) {
// fetch your backend
var list = await axios.get("http://localhost:8000/list");
commit('set', list);
}
}
}
You can read more regarding the fetch method in the nuxtjs docs here
Hope this helps :)

Click on link changes the url but not the content/data on page

the story:
I am on product page #/product/7 and on the same page I have 4 more products that are similar to the one that is being viewed. All these products have links to their pages:
router-link(:to="{ name: 'product', params: { id: product.id }}" v-text='product.title').
the problem:
When I click on any of the product links, the url changes but the content remains the same. So, if I am on #/product/7 and click on #/product/8 the url only will change. If I navigate from /product/:id page and click on a product it takes me to the right page with proper content.
As you can see on screenshot, current product id is 15, but the content is the one from the id 7, as shown in url at the bottom while I was hovering over the Sleek Silk Shirt product in cart.
Any ideas how to fix this?
You have to update the data of products variable when you change the route as vue optimises page reloads and does not reload in your case if you are on same route.
You can adapt the approach: Fetching Before Navigation described in vue-router docs:
With this approach we fetch the data before actually navigating to the new route. We can perform the data fetching in the beforeRouteEnter guard in the incoming component, and only call next when the fetch is complete:
export default {
data () {
return {
product: {},
error: null
}
},
beforeRouteEnter (to, from, next) {
getProduct(to.params.id, (err, product) => {
if (err) {
// display some global error message
next(false)
} else {
next(vm => {
vm.product = product
})
}
})
},
// when route changes and this component is already rendered,
// the logic will be slightly different.
watch: {
$route () {
this.product = {}
getProduct(this.$route.params.id, (err, product) => {
if (err) {
this.error = err.toString()
} else {
this.product = product
}
})
}
}
}
I couldnt really internalise the above answer with 'getProduct', so to be put simply.
I am using a Store and I needed to watch the $route and when it changes I called my store to dispatch the api call.
watch: {
$route () {
this.$store.dispatch('fetchStudyStandards',
this.$route.params.standardID);
}
}
So basically watch the route and if it changes, re do your api call.