Reputation: 385
I'm currently learning how to work with async/await in React JS with hooks. I'm trying to make two API calls to https://restcountries.eu/
. The firs call needs to get information about chosen country's borders. Output is ["CAN", "MEX"]. However, I need to get full names of these borders. That's why I want to make the second call. Since I have an array with two items, I need to make a new API call for each of them. I have been stuck with this for a while. What is the proper way to make the second call considering that I have an array from the first call?
import React, { useEffect, useState } from "react";
import axios from "axios";
const App = () => {
const [countryDetails, setCountryDetails] = useState({});
const [borderNames, setBorderNames] = useState([]);
//FIRST API CALL
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios(
`https://restcountries.eu/rest/v2/name/usa?fullText=true`
);
setCountryDetails(response.data[0]);
} catch (err) {
console.log(err);
}
};
fetchData();
}, []);
//SECONS API CALL - doens't work
useEffect(() => {
let borderName = [];
countryDetails.borders.forEach(async border => {
try {
const response = await axios(
`https://restcountries.eu/rest/v2/alpha?codes=${border}`
);
borderName.push(response.data[0].name);
} catch (err) {
console.log(err);
}
});
setBorderNames(borderName);
}, [countryDetails]);
console.log(countryDetails.borders); //Output: ["CAN", "MEX"]
console.log(borderNames); //Output needs to be: ["Canada", "Mexico"]
return (
<div>
<h2>App</h2>
</div>
);
};
export default App;
Upvotes: 0
Views: 1564
Reputation: 651
A simple approach would be:
useEffect(() => {
const exec = async () => {
const borderNames = await Promise.all(countryDetails.borders.map(border => {
return axios.get(`https://restcountries.eu/rest/v2/alpha?codes=${border}`
}))
setBorderNames(borderNames.map(({data}) => data[0].name));
};
exec();
}, [countryDetails]);
Upvotes: 2
Reputation: 4528
Your structure has some minor issue, check this example you will find out:
const App = () => {
const [countryDetails, setCountryDetails] = React.useState({});
const [borderNames, setBorderNames] = React.useState([]);
React.useEffect(() => {
fetch(`https://restcountries.eu/rest/v2/name/usa?fullText=true`)
.then((res) => res.json())
.then((data) => setCountryDetails(data[0]))
.catch((err) => console.log(err));
}, []);
React.useEffect(() => {
let borderName = [];
if (countryDetails.borders) {
countryDetails.borders.forEach((border) =>
fetch(`https://restcountries.eu/rest/v2/alpha?codes=${border}`)
.then((res) => res.json())
.then((data) => {
borderName.push(data[0].name);
if (borderName.length === countryDetails.borders.length)
setBorderNames(borderName);
})
.catch((err) => console.log(err))
);
}
}, [countryDetails]);
return (
<div>
<p>{JSON.stringify(countryDetails.borders)}</p>
<p>{JSON.stringify(borderNames)}</p>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Upvotes: -1
Reputation: 4623
Your second fetch is async and so, because you don't await
it, the useEffect
sets the borderNames
as an empty array (initial value), before the response arrives.
Apart from await
ing the response, you can just make this change inside your try
block:
const response = await axios(
`https://restcountries.eu/rest/v2/alpha?codes=${border}`);
setBorderNames(prevState => prevState.push(response.data[0].name));
Upvotes: 0