Why whenever I type something that does not exist, i got this error? - reactjs

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);
}
};

Related

How to navigate based on state using react router

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);
}
}

Handling 404 Error Response with Async/Await

I am working on a weather app and need to properly handle a 404 response from the server. There are 2 API requests made with the second one needing data from the first one.
I basically want to render "location does not exist" when there is a 404 error response. An attempt was made with try..catch which resulted in this issue: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'coord').
Error happens for both success and failure responses.
Questions:
What does this error mean and how can I properly de-structure coord prop?
How can I properly setup try..catch to handling error response?
Bonus question: how can try..catch be made inside getForecastData function as well?
Here is the useForecast.js file containing logic and API calls:
try...catch attempt was made in getCoordinates function
import axios from "axios";
const BASE_URL = "https://api.openweathermap.org/data/2.5";
const API_KEY = process.env.REACT_APP_API_KEY;
const useForecast = () => {
// const [forecast, setForecast] = useState(null)
// const [isError, setError] = useState(false)
const getCoordinates = async (location) => {
try {
//try statement
const { data } = await axios(`${BASE_URL}/weather`, {
params: { q: location.value, appid: API_KEY }
});
console.log("call is successful", data);
} catch (data) {
//catch statement
if (!data.ok) {
console.log("location does not exist", data.message);
return;
}
return data;
}
};
const getForecastData = async (lat, lon) => {
const { data } = await axios(`${BASE_URL}/onecall`, {
params: { lat: lat, lon: lon, appid: API_KEY }
});
//if no data is not returned, call setError("Something went wrong") and return
return data;
};
const submitRequest = async (location) => {
const response = await getCoordinates(location);
const { lat, lon } = response.coord;
if (!response || !lat || !lon) return;
console.log("getCoordinates call will render", { response });
const data = await getForecastData(lat, lon);
if (!data) return;
console.log("getForecastData call will render", { data });
};
return {
submitRequest
};
};
export default useForecast;
Here is a stripped down version of the app(where screen shots were generated from): https://codesandbox.io/s/practical-pare-uc65ee?file=/src/useForecast.js
Note: API key has been removed for privacy reasons(sorry for the inconvenience)
Lastly, for context I am using the follow with React in app:
OpenWeather API: https://openweathermap.org/
Axios: https://github.com/axios/axios
You're catching the error successfully. The problem is that when it happens, you are not returning any value to
const response = await getCoordinates(location);
response will then be undefined, and coord will therefore trigger the error since undefined values can't hold any property.
To fix it, you can use the classic safety as below:
const response = await getCoordinates(location) || {};
Which essentially will make response always an object, successful or not
In addition to suggestions from #Houssam and #ale917k adjustments also had to be made with conditionals in submitRequest.
All adjustments made were:
placing return data inside try block
appending || {} to response
changing first if statement to if(!response.coord) then de-structure lat and lon.
Codebase with changes:
import axios from "axios";
const BASE_URL = "https://api.openweathermap.org/data/2.5";
const API_KEY = process.env.REACT_APP_API_KEY;
const useForecast = () => {
// const [forecast, setForecast] = useState(null)
// const [isError, setError] = useState(false)
const getCoordinates = async (location) => {
try {
const { data } = await axios(`${BASE_URL}/weather`, {
params: { q: location.value, appid: API_KEY }
});
console.log("call is successful", data);
//adjustment 1
return data;
} catch (data) {
if (!data.ok) {
console.log("location does not exist");
return;
}
}
};
const getForecastData = async (lat, lon) => {
try {
const { data } = await axios(`${BASE_URL}/onecall`, {
params: { lat: lat, lon: lon, appid: API_KEY }
});
return data;
} catch (data) {
if (!data.ok) {
console.log("something went wrong");
return;
}
}
};
const submitRequest = async (location) => {
const response = (await getCoordinates(location)) || {}; //adjustment 2
//adjustment 3
if (!response.coord) return;
const { lat, lon } = response.coord;
const data = await getForecastData(lat, lon);
if (!data) return;
};
return {
submitRequest
};
};
export default useForecast;
Screenshot of success and failure logs:

Handle fetch api errors in useEffect and set error

I have useEffect in one of my components that fetches data from an api.
I wanna handle two types of errors -
Api is unavailable (say server is down)
Request made is invalid (request made to incorrect endpoint)
And, call setError when any of these errors happens.
This is how I solved it -
const [error, setError] = useState(null)
useEffect(() => {
const fetchModels = async () => {
let res
try {
res = await fetch('http://localhost:8000/api/models/')
}
catch (e) {
if (e instanceof (TypeError)) {
setError('Could not fetch data. Please try again')
return
}
}
if (!res.ok) {
setError('Could not fetch data. Please try again')
return
}
const data = await res.json()
setModels(data)
setError(null)
}
fetchModels()
}, [])
Even though this works, I really believe there must be a better way to do what I am trying to accomplish.

Get type error cannot destructure property as it is undefined in React

I have a MongoDB collection and inside this collection, there are some documents. In these documents, I stored some IDs of another collection documents. This is an image of this document.
In the frontend, I access this document and get the postId. I tried this way.
const onePostId=posts.postId
console.log(onePostId);
const type=typeof (onePostId);
console.log(type);
This code part gives me this result.
I try to pass this postId to an API const response = await axios.get(`/buyerGetOnePost/${onePostId}`) like this way. But this postId is a string type I think that's why I can't get results from this API. Then I try like this const {onePostId}=posts.postId then I get an error that says "TypeError: Cannot destructure property 'onePostId' of 'posts.postId' as it is undefined". How do I solve this problem?
This is the complete code that I tried.
function PostsLocation() {
const { offerId } = useParams();
console.log(offerId);
const [posts, setPosts] = useState({});
useEffect(()=>{
getOnePost();
}, []);
const getOnePost = async () => {
try {
const response = await axios.get(`/buyerGetOneSellerOffer/${offerId}`)
console.log(response);
const allPost=response.data.oneOffer;
setPosts(allPost);
} catch (error) {
console.error(`Error: ${error}`)
}
}
console.log(posts);
const onePostId=posts.postId
console.log(onePostId);
const type=typeof (onePostId);
console.log(type);
const [offerPosts, setOfferPosts] = useState({});
useEffect(()=>{
getOneOfferPost();
}, []);
useEffect(()=>{
if (offerPosts && offerPosts.location) {
console.log(offerPosts.location);
console.log(offerPosts.location.longitude);
console.log(offerPosts.location.latitude);
}
}, [offerPosts]);
const getOneOfferPost = async () => {
try {
const response = await axios.get(`/buyerGetOnePost/${onePostId}`)
console.log(response);
const allOfferPost=response.data.onePost;
setOfferPosts(allOfferPost);
} catch (error) {
console.error(`Error: ${error}`)
}
}
console.log(offerPosts);
const long = offerPosts?.location?.longitude;
console.log(long);
const lat=offerPosts?.location?.latitude;
console.log(lat);
const location={lat,long};
}
Below image shows the results after console.log(posts).
You are trying to destructure a value without getting the data first. So, you are getting a error.
You are running the below code for posts at starting when it doesn't have any data
const onePostId=posts.postId
and only after the call to
getOnePost();
your data gets filled but there is a time delay which you should always consider with async await syntax and you should first check if posts contain posts.postId with some value or if defined and then do the destructuring. If it doesn't then probably don't use it either wait for it or return loading or return mock value probably.
eg:
if(!posts.postId){
// posts.postId is not set and it doesn't have data
}

Axios Error Networ error on request Google place api

im trying to make a request to google api but returns me network error. If i put the url in the browser, brings me the information correctly.I tryed to formate the request without success. The google places search works correctly too.
export const fetch_information = (skip, limit, filter) => async (dispatch) => {
try {
var url = `https://maps.googleapis.com/maps/api/place/details/json?place_id=ChIJk0aJYPbk3JQRLpKN20Jecko&fields=name,rating,formatted_phone_number&key=MyKey`;
const {data} = await axios.get(url)
console.log(data)
} catch (error) {
console.log(error.message)
}
}
and
export const fetch_information = (skip, limit, filter) => async (dispatch) => {
try {
var url = `https://maps.googleapis.com/maps/api/place/details/json?`;
let config = {
params: {
place_id: 'ChIJk0aJYPbk3JQRLpKN20Jecko',
key: 'myKey',
},
}
const {data} = await axios.get(url, config)
console.log(data)
} catch (error) {
console.log(error.message)
}
}
I think that the request looks a bit messy. I'm under the impression that you are trying to pass results to a redux store. Let's see if we can clean this up a bit.
export const fetch_information = async () => dispatch => {
const req = await axios.get("https://maps.googleapis.com/maps/api/place/details/json?place_id=ChIJk0aJYPbk3JQRLpKN20Jecko&fields=name,rating,formatted_phone_number&key=MyKey");
const data = await req.json();
return data;
//or, for your purpose...
console.log(data);
//can also dispatch for store
}
I didn't see anything you were passing as necessary for this.

Resources