I am unable to run the following code where I try to fetch data from a URL. It is going to catch block always despite getting response from the server.
useEffect(() => {
const fetchSomeData = async () => {
try {
await fetch(api);
if (!response.ok) {
throw Error(response.statusText);
}
const json = await response.json();
setData(json);
} catch (error) {
console.error(message);
};
}
fetchSomeData();
}, [api]);
What is response.ok? In your code it is undefined since I don't see response being defined anywhere, therefore the check fails and you will throw an error, which is subsequently being caught by catch.
I suppose you meant to assign the response from the fetch, i.e.:
try {
// Assign the resolved promise payload to `response` const
const response = await fetch(api);
if (!response.ok) {
throw Error(response.statusText);
}
const json = await response.json();
setData(json);
} catch (error) {
console.error(message);
};
Related
In my react project, I'm using fetch api to get the user profile from backend. If there is any error occurred in the API call I'm showing it on the screen.
const navigate = useNavigate();
const [errorMessage, setErrorMessage] = React.useState("");
...
const handleGetProfile = async () => {
await fetch(`${API_URL}/profile`).then(...).catch(err=>setErrorMessage(err.message))
!errorMessage && navigate("/");
}
I wanted to navigate to root path only if no error occurred in the api call. So I'm checking if the error is empty and navigating to the root path.
The problem with this approach is that the setErrorMessage does not guarantee immediate update because it schedules the state update, so it is always navigating to the root path even if there is an error.
How do I solve this issue, any suggestions?
Correct, because React state updates are asynchronously processed, and treated as const the errorMessage state won't have updated inside the handleGetProfile callback.
const handleGetProfile = async () => {
await fetch(`${API_URL}/profile`)
.then(...)
.catch(err => setErrorMessage(err.message));
!errorMessage && navigate("/");
}
It's also anti-pattern to mix async/await with Promise chains. Generally you use one or the other.
To resolve you should move the navigate call into the "resolved" part of the logic. Since fetch returns a Promise and only rejects on network errors you need to also check the response status.
See Checking that the fetch was successful
A fetch() promise will reject with a TypeError when a
network error is encountered or CORS is misconfigured on the
server-side, although this usually means permission issues or similar
— a 404 does not constitute a network error, for example. An accurate
check for a successful fetch() would include checking that the
promise resolved, then checking that the Response.ok property has
a value of true. The code would look something like this:
fetch('flowers.jpg')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not OK');
}
return response.blob();
})
.then(myBlob => {
myImage.src = URL.createObjectURL(myBlob);
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
});
Using Promise chain
const handleGetProfile = () => {
fetch(`${API_URL}/profile`)
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not OK');
}
// handle any successful response stuff
navigate("/");
})
.catch(err => {
setErrorMessage(err.message || err);
});
}
Using async/await with try/catch
const handleGetProfile = async () => {
try {
const response = await fetch(`${API_URL}/profile`);
if (!response.ok) {
throw new Error('Network response was not OK');
}
// handle any successful response stuff
navigate("/");
} catch(err) {
setErrorMessage(err.message || err);
}
}
Use an useEffect hook to response to state changes
const navigate = useNavigate();
const [isFetched, setIsFetched] = React.useState(false);
const [errorMessage, setErrorMessage] = React.useState("");
useEffect(() => {
if (isFetched && !errorMessage) {
navigate("/");
}
}, [errorMessage, isFetched, navigate]);
...
const handleGetProfile = async () => {
setErrorMessage(null);
setIsFetched(false);
try {
const response = await fetch(`${API_URL}/profile`);
if (!response.ok) {
throw new Error('Network response was not OK');
}
// handle any successful response stuff
navigate("/");
} catch(err) {
setErrorMessage(err.message || err);
} finally {
setIsFetched(true);
}
}
Here's how I tried to do it, but it didn't work
useEffect(() => {
try {
const response = async() => {
await axios.get('https://prectik-87c20-default-rtdb.europe-west1.firebasedatabase.app/quizes.json')}
console.log(response.data)
} catch (e) {
console.log(e);
}
})
And so it should look in class components
async componentDidMount() {
try {
const response = await axios.get('https://prectik-87c20-default-rtdb.europe-west1.firebasedatabase.app/quizes.json')}
console.log(response.data)
} catch (e) {
console.log(e);
}
}
You are defining your response as a function (that is never used) rather to do a request call .
Try to split you request function and the useEffect like this (maybe the useEffect don't permit async functions as its parameter).
Maybe this is the correct way to do what you want.
async function request(){
const response = await axios.get('https://prectik-87c20-default-rtdb.europe-west1.firebasedatabase.app/quizes.json')
console.log(response.data)
}
useEffect(async () => {
try {
request()
} catch (e) {
console.log(e);
}
})
I believe you forgat to use the response to convert it to a useable data
useEffect(() => {
try {
const response = async() => {
await axios.get('https://prectik-87c20-default-rtdb.europe-west1.firebasedatabase.app/quizes.json')}
const dataUser = await response.json(); //THIS LINE
console.log(dataUser)
} catch (e) {
console.log(e);
}
})
And so it should look in class components
async componentDidMount() {
try {
const response = await axios.get('https://prectik-87c20-default-rtdb.europe-west1.firebasedatabase.app/quizes.json')}
const dataUser = await response.json(); //THIS LINE
console.log(dataUser)
} catch (e) {
console.log(e);
}
}
I got a few examples in this Github repository
you must define a function and call it after
useEffect(() => {
const fetchData=async()=>{
try {
const response = await axios.get('https://prectik-87c20-default-rtdb.europe-west1.firebasedatabase.app/quizes.json')
console.log(response.data)
} catch (e) {
console.log(e);
}
};
fetchData();
})
Whenever I type something that does not exist in the json I got this error:
TypeError: countries.map is not a function
The search functionality works fine until I type in a result that doesn't exist.
const mainUrl = `https://restcountries.eu/rest/v2/`
const all = `${'all'}`
const serachUrl = `${'name/'}`
const Home = () => {
// usesstate to conutries
const [countries, setCountries] = useState([])
// usesstate to query
const [query, setQuery] = useState('')
{
/* // fetch countries */
}
const fetchCountries = async () => {
let url
if (query) {
url = `${mainUrl}${serachUrl}${query}`
} else {
url = `${mainUrl}${all}`
}
try {
const response = await fetch(url)
const data = await response.json()
setCountries(data)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
fetchCountries()
}, [query])
Issue
When you search for something that doesn't exist the API is returning an error object, a 404.
{
"status": 404,
"message": "Not Found"
}
This is stored in countries state and you then attempt to map it, OFC throwing the error.
Solution
Checking that the fetch was successful
A fetch() promise will reject with a TypeError when a network error is
encountered or CORS is misconfigured on the server-side, although this
usually means permission issues or similar — a 404 does not constitute
a network error, for example. An accurate check for a successful
fetch() would include checking that the promise resolved, then
checking that the Response.ok property has a value of true.
The fetch API returns a resolved Promise even for 400 responses. You should check that the request was successful.
const fetchCountries = async () => {
let url;
if (query) {
url = `${mainUrl}${serachUrl}${query}`;
} else {
url = `${mainUrl}${all}`;
}
try {
const response = await fetch(url);
if (!response.ok) { // <-- check OK response
throw new Error("Network response was not ok");
}
const data = await response.json();
setCountries(data);
} catch (error) {
console.log(error);
}
};
I have an aysnc function that fetches data and returns the value of Promise.all i.e.
const fetchDownloadMenuData = async selectedItems => {
return Promise.all(
selectedItems.map(async item => {
try {
if (item.id === 'interviewGuide') {
const interviewGuide = await positionCandidatesService.getCandidate(
positionId,
candidateSelected.id
);
return interviewGuide.interviewGuide;
}
if (item.id !== 'overview' && item.id !== 'profile') {
const outcome = await outcomesService.get(candidateSelected.id, item.id);
return outcome;
}
return null;
} catch (err) {
appInsights.trackException({
error: new Error(`Error fetching candidate outcome: ${JSON.stringify(err)}`)
});
return null;
}
})
)};
I call this function like this:
try {
downloadData = await fetchDownloadMenuData(selectedItems);
} catch (err) {
appInsights.trackException({
error: new Error(`Could not fetch all PDF data: ${JSON.stringify(err)}`)
});
return;
}
But it never goes into the catch. Why is this? How do i get it to reject if all the promises don't resolve?
Thanks
You're not rejecting in the map...catch block, instead you're returning the null. That's why Promise.all not able to catch the exception. You need use throw in the catch block, Remember, throw will terminate the execution flow.
Try like this
selectedItems.map(async item => {
try {
...async stuff
} catch (err) {
appInsights.trackException({
error: new Error(`Error fetching candidate outcome: ${JSON.stringify(err)}`)
});
throw err OR throw new Error(err) <-- this is missing earlier.
}
})
I am using an Axios cancel request which I thought was to prevent this error yet I am still getting it. Basically I enter a screen where it is fetching data from the server. If I go back to the previous screen before it has finished loading I get the error.
useEffect(() => {
const ourRequest = Axios.CancelToken.source();
setLoading(true);
const getPub = async () => {
try {
const response = await Axios.get("https://.....", {
cancelToken: ourRequest.token,
});
if (response.data) {
const info = await Axios.get(
'http://anotherurl', {
cancelToken: ourRequest.token,
});
setPub(pubInfo.data);
setLoading(false);
}
} catch (err) {
setError(err);
setLoading(false);
}
};
getPub();
return () => {
ourRequest.cancel();
};
}, []);
The request was successfully canceled and it will go to catch block, however the catch block performed a setState "setLoading(false)" but the component was already unmounted. You may performed a condition on catch block to check if the request was canceled.
try {
...
} catch (err) {
if(!request is canceled){
setError(err);
setLoading(false);
}
}