I'm trying to make a weather application and I am stuck using the async/await function.
I have a function which does a axios call to an api and then I'm trying to set some state variables. The think is that it seems the application is running the setState methods before the promise is resolved.
Here is the method that makes the axios call.
const fetchWeeklyWeather = async () => {
try {
let response = await axios.get(url);
console.log('%cFetch Weekly Weather Response:', 'color: #bada55', response);
setCurrentWeather(response.data.current);
setWeeklyWeather(response.data.daily);
setWeatherAlerts(response.data.alerts);
} catch (err) {
// Handle Error Here
console.error(err);
}
}
And I call that method on the ponentDidMount like so:
useEffect(() => {
fetchWeeklyWeather();
console.log("Current Weather", currentWeather);
console.log("Weekly Weather", weeklyWeather);
console.log("Weather alerts", weatherAlerts);
formatData(weeklyWeather);
}, []);
It is weird because sometimes it works but most of the time it does not. I guess I'm not doing the right think with the async/await. Can anyone give me a hand?
I'm trying to make a weather application and I am stuck using the async/await function.
I have a function which does a axios call to an api and then I'm trying to set some state variables. The think is that it seems the application is running the setState methods before the promise is resolved.
Here is the method that makes the axios call.
const fetchWeeklyWeather = async () => {
try {
let response = await axios.get(url);
console.log('%cFetch Weekly Weather Response:', 'color: #bada55', response);
setCurrentWeather(response.data.current);
setWeeklyWeather(response.data.daily);
setWeatherAlerts(response.data.alerts);
} catch (err) {
// Handle Error Here
console.error(err);
}
}
And I call that method on the ponentDidMount like so:
useEffect(() => {
fetchWeeklyWeather();
console.log("Current Weather", currentWeather);
console.log("Weekly Weather", weeklyWeather);
console.log("Weather alerts", weatherAlerts);
formatData(weeklyWeather);
}, []);
It is weird because sometimes it works but most of the time it does not. I guess I'm not doing the right think with the async/await. Can anyone give me a hand?
Share Improve this question edited Aug 24, 2023 at 21:22 desertnaut 60.4k32 gold badges152 silver badges180 bronze badges asked Feb 9, 2022 at 22:15 Adri FeriaAdri Feria 611 gold badge1 silver badge4 bronze badges 3- Yes, you don't use async-await correctly. fetchWeeklyWeather returns a promise, and you don't wait for it. Another problem is what the answer says, state updates can be asynchronous too. You should either access weeklyWeather in another effect, or just return data you need from fetchWeeklyWeather and don't rely on the state here – Estus Flask Commented Feb 9, 2022 at 23:00
-
"you don't use async-await correctly" OPs use of async/await is perfectly fine. "fetchWeeklyWeather returns a promise, and you don't wait for it" pointless. The effect-function can not be async, and even
then()
is not an option because the state would still not have been updated when that runs. "state updates can be asynchronous too" They are, always! That's why awaiting offetchWeeklyWeather()
is no option. In FunctionComponents there is no notification that tells you when the state has been updated. The Component re-renders, why? Only React knows. – Thomas Commented Feb 10, 2022 at 3:14 - Thanks guys for you answers. I'll try to fix it this evening and see if i get something. – Adri Feria Commented Feb 10, 2022 at 16:53
3 Answers
Reset to default 1useEffect(() => { fetchWeeklyWeather(); console.log("Current Weather", currentWeather); console.log("Weekly Weather", weeklyWeather); console.log("Weather alerts", weatherAlerts); formatData(weeklyWeather); }, []);
The callback is only executed on ponent mount (empty dependancy array), so the console.log()
statements will only log the initial values of currentWeather
, weeklyWeather
and weatherAlerts
, not the values that are set retrieved by axios
.
To fix this you should separate the useEffect
callback into multiple callbacks.
// runs on ponent mount
useEffect(() => {
fetchWeeklyWeather();
}, []);
// runs on ponent mount and whenever currentWeather changes
useEffect(() => {
console.log("Current Weather", currentWeather);
}, [currentWeather]);
// runs on ponent mount and whenever weeklyWeather changes
useEffect(() => {
console.log("Weekly Weather", weeklyWeather);
formatData(weeklyWeather);
}, [weeklyWeather]);
// runs on ponent mount and whenever weatherAlerts changes
useEffect(() => {
console.log("Weather alerts", weatherAlerts);
}, [weatherAlerts]);
The above should log currentWeather
twice. Once with the initial value, and once with the value that is loaded by axios
. The same applies for weeklyWeather
and weatherAlerts
.
Note that formatData()
is also called twice. So it must be able to handle the call with the initial data. If this initial data is null
you might want to skip the initial call.
const [weeklyWeather, setWeeklyWeather] = useState(null);
// initial weeklyWeather value ^^^^
// ...
// runs on ponent mount and whenever weeklyWeather changes
useEffect(() => {
console.log("Weekly Weather", weeklyWeather);
// skip the formatData() call if weeklyWeather is null
if (weeklyWeather) formatData(weeklyWeather);
}, [weeklyWeather]);
The above will skip the formatData()
call when weeklyWeather
is null
(or any other falsy value). Meaning that it will not be called for the initial value, but it is called once the axios
response updated the state.
because your function named 'fetchWeeklyWeather' is an async function, you call the 'fetchWeeklyWeather' in your 'useEffect' hook as a sync function, so you can't log right result below.
you should call like this:
useEffect(()=>{
fetchWeeklyWeather()
.then(()=>{
// do the console.log
})
.catch(err=>console.error(err))
},[]);
solved this
this.setState((prevState) => {return {page: prevState.page + 1});