React-Query, error on mutation despite response 204 - reactjs

I'm trying to use a delete method of my api with react-query. I've tried this:
const { isLoading, mutate } = useMutation(() => api.remove(path, item.id), {
onSuccess: () => {
openModal();
refetchData();
},
});
...
<button onClick={() => mutate()}>Remove element</button>
But when it's execute i'm getting this error message:
SyntaxError: Unexpected end of JSON input at Api.tsx:104
It's not a problem due to my api beacause despite this error message, the opperation is completed on database.
I've test it on Insomnia and it's working, i'm getting a 204.
Did I make somthing wrong on my code ?
Thanks in advence!

You are not showing what api.remove is doing, so I have to guess there is some json parsing there that errors.
react-query doesn't care about status codes, it cares about a resolved promise (results in success state) or a rejected promise (results in error state). If your client side api function has some javascript runtime error, it will result in error state because of it.

Related

why the try..catch state in recoil selectorFamily catch promise object?

i'm using recoil selectorFamily witch subscribes several selector or selectorFamily for data query.
and also using the try...catch for tracking each data query's status.
and then i figured out that CATCH state catch the promise object on default excution.
so it makes the function calling dataQuery consider it exception but there is no exception.
i wonder why this happend.
and also how can i sure if promiseState loged on browser is fulfilled or pending?
i'm confusing cause it's marked <pending> but it's promiseState property saying 'fulfilled'.
here is code and browser log as result
const dataQueryForPage = selectorFamily<{data:IPageData; message:string|null;status:number},number>({
key:'dataQueryForPage',
get:(refreshKey)=> async({get})=>{
try{
const data1 = await get(data1Query);
const data2 = await get(data2Query);
const data3 = await get(data3Query);
...
}catch(error){
console.log('---------------------------------error', error);
if (error instanceof Promise) {
error
.then(() => {
console.log('--------------------------------its resolved');
})
.catch(() => {
console.log('-------------------------------its rejected');
});
...
}
})
Why are Promises caught?
The get function provided by the parameters of selector callback throws them.
If get receives a pending atom, it throws a Promise to interrupt the evaluation. The evaluation will be restarted when the thrown Promise is resolved.
Source code:
https://github.com/facebookexperimental/Recoil/blob/main/packages/recoil/recoil_values/Recoil_selector.js#L724
And get does NOT return a Promise but the awaited value. So it's not necessary to put an await before get(data1Query)
Inconsistent PromiseState
The console evaluates lazily.
In your case, each state was evaluated twice. The first time was the Promise being logged to console. The second time was you expanded the properties.
The displayed value corresponded to the state at that very moment.

Handling errors conditionally in react-query

I started using react-query a couple of days ago and everything seems amazing but I don't understand how I can handle errors returned from the server with their status code and error key
lets take the peace of code below as an example
const onError = (error) => {
console.log('error occurred', error)
}
let { id } = useParams()
const { data: User, isLoading, isError, error, isRefetching, status, refetch } = useQuery(['get-user-by-id', id], getUserById(id), {
onError
})
in this scenario when the API returns an error the onError function does not fire furthermore when i try to render a toast containing the {error} the message is Missing queryFn and when rendering {error?.message} the message is just Error
i would like to be able to get the message sent from the server with its key, ex.
if(error?.response.status === 404){
if(error?.response?.data?.detail){
let error = error?.response?.data?.detail
}else if(error?.response?.data?.message){ //another error key that might return
let error = error?.response?.data?.message
}// and so on...
}else if (error?.response?.status === 400){} // and so on...
or a key that I know my API would return depending on the status code this is especially critical for forms, while a get request could be fine with simple non-detailed error messages, a post request might hold relevant information about the error that can help the user understand it like if a name for a certain field is already taken or their is complex validation involved in the serverside, how can i handle error's in the way i explained above?
Your problem is, according to your code, that you are not passing a function as the query function parameter, but you are executing the function during render:
useQuery(['get-user-by-id', id], getUserById(id),
unless this was a typo, what you need is:
useQuery(['get-user-by-id', id], () => getUserById(id),
but given that you said you get Missing queryFn as error, I'm pretty sure this is it.

Getting error "Unhandled Rejection (TypeError): api_call4.json is not a function" even when error is caught in reactjs

While fetching the weather api, if disconnection occurs, error is thrown which is caught, and few seconds later the error is displayed on screen which is quite unpleasant. No idea of resolving this error.
App.js file
const api_call4 = await fetch(`https://api.weatherbit.io/v2.0/current?` +
`city=${city}&key=${API_KEY3}`).catch(error => toast.error('No Data Received',error))
const data4 = await api_call4.json();
console.log('DATA CURRENT', data4)
As you can see above error is caught, but still error is diplayed. Please see the image below. What is the best solution...
You have to change your flow to ensure that your api call is successful. One way would be to use try/catch blocks since you're using async/await. Something like:
try {
const api_call4 = await fetch(`https://api.weatherbit.io/v2.0/current?` +
`city=${city}&key=${API_KEY3}`)
const data4 = await api_call4.json();
console.log('DATA CURRENT', data4)
}
catch(error) {
toast.error('No Data Received',error)
}

Connected-React-Route change still causes typeError to appear

I am using connected-react-router history to navigate to the login screen when a users authentication times out. To do this I use axios interceptors to catch all responses and check to see if they're returning an error message that indicates the above. I am doing this as shown below:
this.client.interceptors.response.use(response => response, error => {
if(error.response.status === 401 && store && store.getState().user){
store.dispatch(actions.resetUser())
history.push('/login?authErr')
}
Promise.reject(error.response)
})
The routing works just fine however, react still try's to render the component I navigated away from. Since the back-end responded with an error the data the component receives is null which causes the application to throw a Type Error and crash.
Since this can occur on any api call in the application after auth timeout is there a way I can suppress this render? or reset the application? Do I have to wrap all my api calls in a try, catch?
I figured it out. While I was editing my code I removed the return from in front of the promise.reject. So when the response was being returned as an error it wasn't being handled.
this.client.interceptors.response.use(response => response, error => {
if(error.response.status === 401 && store && store.getState().user){
store.dispatch(actions.resetUser())
history.push('/login?authErr')
}
return Promise.reject(error.response)
})

How to handle api errors using aws-amplify?

I'm currently trying to POST data to my aws lambda functions triggered by aws api-gateway using the aws-amplify react lib.
Here is the code :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(console.log(err))
In the main case, everything is OK.
But my lambda function is design to validate the input data and return a status code 400 with a returned payload looking like that :
{
"errors": [
{
"field": "title",
"message": "This field is required"
}
]
}
I would like to catch those errors in order to display them in the frontend but aws-amplify seems to have an undocumented behavior.
By default, status code 400 returned are throw with a default error message :
Error: Request failed with status code 400
at createError (createError.js:16)
at settle (settle.js:18)
at XMLHttpRequest.handleLoad (xhr.js:77)
Is there a way to get the returned payload instead of this magical error?
It turns out that under the hood, aws-amplifyuse Axios to make http calls.
When using Axios, you have to console.log(error.response): https://github.com/axios/axios/issues/960
Here is the fix I've made :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(error => console.log(error.response.data))
A Pull Request on the aws-amplify documentation is open : https://github.com/aws/aws-amplify/pull/633
I also faced the similar issues, It showed the default error message "Request failed with status code 400", instead of the message that is returned from API.
I logged the Error object and it did not show the response attribute in it. But we do have response attribute. I tried logging the Error.response and it did contain the response sent from the API.
Just figured out this by going through the 'Cancel API requests' Amplify docs.
From what I can see this is the contents of the error object returned by the API call:
Heres what I am doing to just print out the error, obviously you would do a lot more here but its a good start.
async uploadUser(state, payload) {
const promise = API.graphql({
query: createUser,
variables: { input: payload },
});
try {
await promise;
} catch (error) {
// Print out the actual error given back to us.
console.log(error.errors[0].message);
// If the error is because the request was cancelled we can confirm here.
if (API.isCancel(error)) {
// handle user cancellation logic.
console.log(error.message);
}
}
Hope that helps 😃

Resources