How to send a response in a callBack - reactjs

I'm pretty new to the fetch api and cant figure out why I can access response.json() locally but not once deployed.
EDIT: Just making the question clearer. When my response is NOT ok, I want to send response.json() to my errorCallBack function. This works locally and I set my error message to 'MyException'. However, when it is deployed to a new environment the parameter received by errorCallBack is a Promise with the status - 'Pending' and the catch in the errorCallBack function is hit.
Fetch:
fetch(request)
.then(response => {
if (!response.ok) {
errorCallBack(response.json());
return;
}
successCallBack();
})
.catch(error => {
console.log(error.message);
errorCallBack();
});
Error callback:
errorCallBack = (jsonResponse) => {
jsonResponse
.then(data => {
if (data) {
if (data.MyException) {
this.setErrorMessage("MyException");
return;
}
}
})
.catch(() => {
this.setErrorMessage("");
});
}

Some changes. response.json() can fail if you are returning an empty response, but you can add your logic there.
fetch(request)
.then(response => {
if (!response.ok) {
console.log(response.status) // Error code
console.log(response.statusText); // Error message
return errorCallBack(response.json()); // return response Promise
}
return successCallBack(response.json()); // This will return a Promise
})
.catch(error => {
console.log(error); // Log any error
errorCallBack(error); // return an error object
});
handle Error
errorCallBack = (response) => {
response
.then(jsonData => {
if(jsonData.myException) {
...
} else{
...
}
})
.catch(error => {
console.log(error)
...
})
}

Related

Axios does not catch error even not enter in catch block

I am trying to get the error status code that would be 413 in Axios catch block. I have tried different solutions nothing worked for me. Could you please review what is going wrong.
uploadNewDatDocuments(datId, files = [], additionalInfo = {}) {
return new Promise((resolve, reject) => {
let url = new URL(this.baseUrl + this.uploadDocument.replace('{id}', datId));
Object.keys(additionalInfo).forEach(queryParam => url.searchParams.set(queryParam, additionalInfo[queryParam]));
let formData = new FormData();
files.forEach(file => formData.append('files', file));
axios
.post(url.toString(), formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {
resolve(response.data);
})
.catch(error => {
console.log("error occurred")
reject(error);
}).finally(error=>{
console.log(error);
})
});
}
Here is my Action code.
export function uploadNewDocuments(datId, additionalInfo = {}, attachments = [], comment = {}) {
return dispatch => {
datService
.uploadNewDatDocuments(datId, attachments, additionalInfo)
.then(response => {
const attachmentsIds = response.map(attachment => attachment.id);
dispatch(
DatCommentActions.addDatNewComment(datId, {
...comment,
message: { ...comment.message, attachments: attachmentsIds }
})
);
})
.catch(error => {
dispatch(MessageActions.showMessage({ message: error.response.data.message }));
console.error(error);
});
};
}
413 Request Entity Too Large is not actually error, its a not successful response and catch wont fire unless there is actual error on response.
What you could do is check response.status and based on that and write own error handling.

Fetch API React Js Unexpected token < in JSON at position 0

I got this error from my fetch api when I pass my url fetch API from a state called url. But when I changed my url into text like 'api/provinsi' I don't get any error
Error fetching data: SyntaxError: Unexpected token < in JSON at
position 0
useEffect(() => {
async function fetchMyAPI() {
await fetch(url, {
method: 'GET'
}).then(response => {
if(!response.ok) {
throw new Error(response.statusText);
}
return response.json();
})
.then(response => {
if(response.Response === 'False') {
throw new Error(response.Error);
}
setData(response.data);
}).catch(error => {
console.log("Error fetching data: ", error);
});
}
fetchMyAPI();
}, []);
And this is how I set my url state:
useEffect(() => {
if(idDaerah==1){
setColumn(['no', 'nama', 'aksi']);
setUrl('/api/provinsi');
} else if(idDaerah==2) {
setColumn(['no', 'nama', 'provinsi_id', 'aksi']);
setUrl('/api/kabupaten');
} else if(idDaerah==3) {
setColumn(['no', 'nama', 'kabupaten_id', 'aksi']);
setUrl('/api/kecamatan');
} else {
setColumn(['no', 'nama', 'kecamatan_id', 'aksi']);
setUrl('/api/desa');
}
}, []);
Because both useEffect runs at the same time, and the fetch function receive url as an empty string first time, so you need to add url as dependency.
useEffect(() => {
async function fetchMyAPI() {
await fetch(url, {
method: 'GET'
}).then(response => {
if(!response.ok) {
throw new Error(response.statusText);
}
return response.json();
})
.then(response => {
if(response.Response === 'False') {
throw new Error(response.Error);
}
setData(response.data);
}).catch(error => {
console.log("Error fetching data: ", error);
});
}
if(url !== ''){ //check if url is set i.e not an empty string
fetchMyAPI();
}
}, [url]); //add dependency, when url change then call api
OR
You can provide a default url like:
const [url, setUrl] = useState('/api/provinsi');

How to handle bad request in fetch()

I know this is a basic and often recurring issue, but I am still unable to make it work.
I have the following code
handleAdd = (event: any): void => {
// ...
// create new task
try {
// send data to backend
fetch(`/api/tasks?name=${name}&priority=${priority}`, { method: 'post' })
.then(response => { if (!response.ok) {
throw new Error('error => how to get bad request message here?') } })
}
// handle exception
catch (e) {
console.log(e);
this.setState({
isError: true,
errorMessage: e.message
});
}
}
the API returns 400 with some error message
but in the browser I get
So I have two questions
Why the throw new Error() in fetch does not goes to catch(e) {} method (if the error is outside fetch function, it works well)? How to rewrite this line to get into catch method? I think it has something to do with the Promise object?
How to get the bad request message from response object?
update, working solution
fetch(`/api/tasks?name=${name}&priority=${priority}`, { method: 'post' })
.then(response => {
if (!response.ok) {
response.text().then(function (text) {
throw Error(text);
}).catch((e) => {
this.setError(e.message);
});
}
})
how to get response.text() into the Error exception?
when using promises, you can choose between chaining your promise with then/catch or using async/await.
if you are chaining promise you should chain with a catch:
handleAdd = (event: any): void => {
// send data to backend
fetch(`/api/tasks?name=${name}&priority=${priority}`, { method: 'post' })
.then(response => { if (!response.ok) {
throw new Error('error => how to get bad request message here?') } }
).catch((e) => {
console.log(e);
this.setState({
isError: true,
errorMessage: e.message
})
});
}
if you prefer you can change your function to async/await. with that you would use a try/catch block:
// mark your function as async
handleAdd = async (event: any): void => {
try {
// await your fetch response
const response = await fetch(`/api/tasks?name=${name}&priority=${priority}`, { method: 'post' })
if (!response.ok) {
throw new Error('error => how to get bad request message here?')
}
}
// handle exception
catch (e) {
console.log(e);
this.setState({
isError: true,
errorMessage: e.message
});
}
}
I'd imagine if you are not using React, it could be that you have a local or global error or warning message area, so that the
fetch
.then()
.catch(err => {
// display "try again later" here
});
But since you are using React and probably Redux, you could dispatch an action NETWORK_ERROR instead so that the reducer will create that error message "try again later":
fetch
.then()
.catch(err => {
// dispatch the action for NETWORK_ERROR here
});
try it this way and you are good to go
fetch(`/api/tasks?name=${name}&priority=${priority}`, { method: 'post' })
.then(response => { if (!response.ok) {
throw new Error('error => how to get bad request message here?') }
}).catch(error => {
// handle the error here
console.log(e);
});

TypeError: Cannot read property 'setState' of undefined/XML

I am calling service that is using XML, so i want to parse XML to JSON and the JSON data set as my react state but i am getting.
TypeError: Cannot read property 'setState' of undefined
axios
.get(session_url)
.then(function(response) {
parseString(response.data, (err, result) => {
if (err) {
throw err;
} else {
this.setState({
odm: result.data,
loading: false,
});
}
});
})
.catch(function(error) {
console.log(error);
});
You're mixing and matching function()s that don't capture this and arrow (=>) functions which do.
Simply use arrow functions everywhere and the this (that's your React component) will be properly captured:
axios
.get(session_url)
.then(response => {
parseString(response.data, (err, result) => {
if (err) {
throw err;
} else {
this.setState({
odm: result.data,
loading: false,
});
}
});
})
.catch(error => {
console.log(error);
});
Better yet, if you can, use an async function:
// promisified version of `parseString`:
const parseStringP = data =>
new Promise((resolve, reject) =>
parseString(response.data, (err, result) => {
if (err) return reject(err);
resolve(result);
}),
);
// ...
try {
const response = await axios.get(session_url);
const result = await parseStringP(response.data);
this.setState({
odm: result.data,
loading: false,
});
} catch (error) {
console.log(error);
}

Rewrite fetch call to oboe for json streams with Typescript

I have this fetch call:
api<T>(url: string, headers: Request): Promise<T> {
return fetch(url, headers)
.then(response => {
if (!response.ok) {
throw new Error(response.statusText);
}
return response.json().then(data => data as T);
})
.catch((error: Error) => {
throw error;
});
}
componentDidMount(){
this.api<Array<Response>>(url, requestData)
.then(data => {
this.setState({
jobs: data
});
})
.catch(error => {
console.error(error);
});
}
But the response that I get is a stream+json so I get invalid json at .json().
I saw that there is a library that can help me: http://oboejs.com/examples
But I'm having issues using oboe and typescript (beginner) (using https://www.npmjs.com/package/#types/oboe).
I tried:
api<T>(headers: Request): Oboe<T> {
return oboe(headers)
.done(function(response) {
return response;
})
.fail(function(error: Error) {
throw error;
});
}
componentDidMount(){
this.api<Array<Response>>(requestData)
.done(data => {
this.setState({
jobs: data
});
})
.fail(error => {
console.error(error);
});
}
But there are obvious errors as I don't know what type oboe should return so I get an error Oboe is not generic.
The error means that the Oboe class/type is not generic. Like Number of String for example
From Oboe's docs it seems that oboe(param).done() takes a callback
You can transform that call into a Promise and do the rest the same way you used to do
Replacing the callback logic with a Promise
api<T>(headers: Request): Promise<T> {
return new Promise((resolve, reject) => {
oboe(headers)
.done(data => resolve(data))
.fail(err => reject(err));
});
}
Calling it (the way you did with Promise/fetch)
componentDidMount(){
this.api<Array<Response>>(url, requestData)
.then(data => {
this.setState({
jobs: data
});
})
.catch(error => {
console.error(error);
});
}

Resources