I want to make a special component which handles failed fetch requests. It is expected to work in this way:
- If fetch request fails then several more attempts should be made after several seconds.
- This special component should display countdown timer for next request to launch.
So I have:
Fetch function is in store. It works fine (makes 3 requests after 3, 6 and 9 seconds).
import { createStore } from "vuex";
const wait = async (ms) => {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
export default createStore({
state: {
error: {
isError: false,
timerSec: null
}
},
mutations: {
setError(state, payload) {
state.error = payload.error;
}
},
actions: {
async fetchProducts({ commit, dispatch }, attempt = 1) {
try {
const response = await fetch("https://fakestoreapi.com/products222");
if (!response.ok) {
throw new Error("Something went wrong");
}
} catch (e) {
console.log("Request:", attempt);
commit("setError", {
error: {
isError: true,
timerSec: attempt * 3
}
});
if (attempt >= 3) {
return;
}
await wait(attempt * 3000);
return dispatch("fetchProducts", attempt 1);
}
}
}
});
I call fetchProducts() in App.vue on mount. In App.vue I pass following data to error-request component:
<template>
<error-request v-if="error.isError" :timeout="error.timerSec"></error-request>
<h1 v-else>This should be rendered if there's no errors</h1>
</template>
In the error-request component I have a countDown method which triggered when timeout props changes.
<template>
<div>
<h1>The next attempt to fetch data will be made in:</h1>
<h2>{{ timer }}</h2>
</div>
</template>
<script>
export default {
props: ["timeout"],
data() {
return {
timer: null,
interval: null,
};
},
methods: {
countDown(sec) {
this.interval = setInterval(() => {
this.timer = sec;
if (sec === 0) {
clearInterval(this.interval);
return;
}
sec--;
}, 1000);
},
},
watch: {
timeout() {
this.countDown(this.timeout);
},
},
};
</script>
Unfortunatelly countdown timer shows only once and only on second request (it ignores first request with countdown from 3 to 1 and ignores third request. Could you help me to fix it?
I made a codesandbox: https://codesandbox.io/s/peaceful-sinoussi-ozjkq8?file=/src/App.vue
CodePudding user response:
You should do:
methods: {
countDown(sec) {
this.timer = sec;
this.interval = setInterval(() => {
this.timer--;
if (this.timer === 0) {
clearInterval(this.interval);
return;
}
}, 1000);
},
},
watch: {
timeout: {
handler() {
this.countDown(this.timeout);
},
immediate: true,
},
},
There are 2 points to notice:
- Don't modify the function parameters to prevent side effects (In your case is the
sec
parameter) - You have to trigger the watch for the first time so you need to add the option
immediate: true