I'm currently trying out react-query in my project.
I'm having trouble with handling errors within my mutation.
In my networks tab, I can confirm that the server is responding with code 400 or 500, which I assumed makes axios throw an error, thus firing the defined onError function.
However, the onSuccess function is always called no matter how the API call goes.
What am I missing here? Thanks in advance.
const { mutate } = useMutation(
['mutation'],
() => axios.patch(API_URL, params),
{
onSuccess: () => {
//this is always fired, even when response code is 400, 500, etc.
void queryClient.invalidateQueries('query');
},
onError: () => {
//should do something but never fired
},
}
);
A simple approach would be to manage the status code in the onSuccess callback.
It's a bug in the useMutation which I am also facing so I manage the status code in conditionals. But it should work not work in case of 500. There is a
flaw in your implementation then.
Make sure to return the result of your mutation function (i.e. the axios.patch call needs to be returned)
const { mutate } = useMutation(['mutation'], () => return axios.patch(API_URL, params),
{
onSuccess: () => {
//this is always fired, even when response code is 400, 500, etc.
void queryClient.invalidateQueries('query');
},
onError: () => {
//should do something but never fired
},
})
It might be because you're passing arguments in worng order.
In docs it says you should call it as follows:
useMutation(mutationFn, {
mutationKey,
onError,
onMutate,
onSettled,
onSuccess,
retry,
retryDelay,
useErrorBoundary,
meta,
});
Which makes your code as follows:
useMutation(
() => axios.patch(API_URL, params),
{
mutationKey: 'mutation',
onSuccess: () => {
//this is always fired, even when response code is 400, 500, etc.
void queryClient.invalidateQueries('query');
},
onError: () => {
//should do something but never fired
},
}
Related
I am trying to use the inertia useForm.js submit function and I found that the onProgress is execute but console.log didn't print out anything.
Inertia.post('/generateFile', form, {
onProgress: () => {
console.log("onProgress");
openGeneratingFileDialog();
},
onSuccess: () => {
console.log("onSuccess");
openSuccessDialog();
},
onError: () => {
console.log("onError");
opneFailDialog();
},
})
Seems the progress event is only fired when uploading files.
So if you want to use the onProgress, you have to ensure you are uploading a file, otherwise will be useless.
Docs here (Progress section).
I'm working on a project with Vue-CLI, and here's some parts of my code;
//vuex
const member = {
namespaced:true,
state:{
data:[]
},
actions:{
getAll:function(context,apiPath){
axios.post(`http://localhost:8080/api/yoshi/backend/${apiPath}`, {
action: "fetchall",
page: "member",
})
.then(function(response){
context.commit('displayAPI', response.data);
});
},
toggle:(context,args) => {
return axios
.post(`http://localhost:8080/api/yoshi/backend/${args.address}`,
{
action:"toggle",
ToDo:args.act,
MemberID:args.id
})
.then(()=>{
alert('success');
})
},
},
mutations:{
displayAPI(state, data){
state.tableData = data;
},
},
getters:{
getTableData(state){
return state.tableData
}
}
}
//refresh function in member_management.vue
methods: {
refresh:function(){
this.$store.dispatch('member/getAll',this.displayAPI);
this.AllDatas = this.$store.getters['member/getTableData'];
}
}
//toggle function in acc_toggler.vue
ToggleAcc: function (togg) {
let sure = confirm(` ${todo} ${this.MemberName}'s account ?`);
if (sure) {
this.$store
.dispatch("member/toggle", {
address: this.displayAPI,
id: this.MemberID,
act: togg,
Member: this.MemberName,
})
.then(() => {
this.$emit("refresh");
});
}
},
The acc_toggler.vue is a component of member_management.vue, what I'm trying to do is when ToggleAcc() is triggered, it emits refresh() and it requests the updated data.
The problem is , after the whole process, the data is updated (I checked the database) but the refresh() funciton returns the data that hadn't be updated, I need to refresh the page maybe a couple of times to get the updated data(refresh() runs everytime when created in member_management.vue)
Theoretically, the ToggleAcc function updates the data, the refresh() function gets the updated data, and I tested a couple of times to make sure the order of executions of the functions are right.
However, the situation never changes. Any help is appreciated!
The code ignores promise control flow. All promises that are supposed to be awited, should be chained. When used inside functions, promises should be returned for further chaining.
It is:
refresh:function(){
return this.$store.dispatch('member/getAll',this.displayAPI)
.then(() => {
this.AllDatas = this.$store.getters['member/getTableData'];
});
}
and
getAll:function(context,apiPath){
return axios.post(...)
...
I'm trying to do a pretty simple intercept in Cypress using a Vue's application. My component has a setup method using render function as such:
setup() {
useInfiniteLoading({ runner: ... })
}
Then on my tests I do the following:
describe("List todo resource", () => {
it("Checks it loads more todos when scrolling to the bottom", function () {
cy.intercept('/todo').as('getTodos');
cy.visit("/todos");
cy.wait("#getTodos").then(({response}) => {
console.log(response);
})
})
})
When running the test I see that the intercept is not stubbing the response.
As you can see from the image the request makes a request to my actual server running locally and the response is stubed. The weird part is that in a previous test I have:
it("Checks the todo list gets updated when clicking on to resolve it (from true to false)", function () {
cy.visit("/todos");
const resolved = false;
const shouldHaveClass = resolved
? "mdi-checkbox-marked-outline"
: "mdi-checkbox-blank-outline";
cy.intercept("GET", "todo", {
fixture: "resources/todo/list.todo.json",
}).as("getTodos");
cy.intercept("PUT", "todo", {
body: { data: { ...this.updateTodoFixture.data, resolved } },
}).as("updateTodo");
cy.get(".todo-list-item__resolve")
.first()
.each((btn) => {
btn.click();
});
cy.get(".todo-list-item__resolve")
.first()
.should("satisfy", ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes(shouldHaveClass);
});
});
And the response is stubbed using intercept as you can see from the previous screenshot. Is it possible that the previous test is affecting the next test? I have tried taking a look into "Intercept too soon" but no luck on trying to apply the fix described in the page.
Any idea on what could be causing the stub not to happen?
I'm trying to run a function that needs some data that I get back from the mounted method. Right now I try to use computed to create the function but unfortunately for this situation computed runs before mounted so I don't have the data I need for the function. Here is what I'm working with:
computed: {
league_id () {
return parseInt(this.$route.params.id)
},
current_user_has_team: function() {
debugger;
}
},
mounted () {
const params = {};
axios.get('/api/v1/leagues/' +this.$route.params.id, {
params,
headers: {
Authorization: "Bearer "+localStorage.getItem('token')
}
}).then(response => {
debugger;
this.league = response.data.league
this.current_user_teams = response.data.league
}).catch(error => {
this.$router.push('/not_found')
this.$store.commit("FLASH_MESSAGE", {
message: "League not found",
show: true,
styleClass: "error",
timeOut: 4000
})
})
}
As you can see I have the debugger in the computed function called current_user_has_team function. But I need the data I get back from the axios call. Right now I don't have the data in the debugger. What call back should I use so that I can leverage the data that comes back from the network request? Thank You!
If your computed property current_user_has_team depends on data which is not available until after the axios call, then you need to either:
In the current_user_has_team property, if the data is not available then return a sensible default value.
Do not access current_user_has_team from your template (restrict with v-if) or anywhere else until after the axios call has completed and the data is available.
It's up to you how you want the component to behave in "loading" situations.
If your behavior is synchronous, you can use beforeMount instead of mounted to have the code run before computed properties are calculated.
I'm using qwest to query my endpoint as shown below, the onGetResourceCompleted handler fires as expected but data is undefined. Why?
var Actions = Reflux.createActions({
'getResource': { asyncResult: true }
});
Actions.getResource.listenAndPromise(function (id) {
return qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true });
});
var MyStore = Reflux.createStore({
listenables: Actions,
init: function () {
Actions.getResource('');
},
onGetResourceCompleted: function (data) {
console.log('OK', data); // Get's called but data is undefined. Why?
}
});
I can see the data loads correctly by looking at dev tools as well as calling qwest in isolation by simply doing:
qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true }).then(function(data) {
console.log('OK', data);
});
Also doing the following works:
ServiceActions.getResource.listen(function (id) {
ServiceActions.getResource.promise(
qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true })
);
});
I've put some comments on the cause of this "confirmed bug" in the original issue you opened at github.com/spoike/refluxjs.
So, though you are using the reflux features the way they are intended, and they're definitely creating a race condition without even returning the race results, I think you're in luck. It turns out the two particular features you're using in this combination with this type of request is a bit redundant when you already have a promise available. I'd recommend you just drop the onGetRequestCompleted handler entirely, and handle completion using the standard promise ways of handling resolved promises, which honestly will give you more flexibility anyways.
For example:
var MyStore = Reflux.createStore({
listenables: Actions,
init: function () {
Actions.getResource('')
.then() <-- this eliminates the need for onGetResourceCompleted
.catch() <-- or this instead/in addition
.finally() <-- or this instead/in additon
},
// no more onGetResourceCompleted
});