Reputation: 125
I have a function that is run when a user clicks a button, when this function is run it gathers data and updates state. I then have another function which runs that uses some of the data that is added to state, the issue is the state is not updating in time so the function is using old data.
async function callWeather() {
const key = "";
// Get location by user
let location = formData.location;
// Url for current weather
const currentWeatherUrl = `https://api.openweathermap.org/data/2.5/weather?q=${location}&units=metric&appid=${key}`;
// Get the current weather
const currentWeatherResponse = await fetch(currentWeatherUrl);
if (!currentWeatherResponse.ok) {
// Return this message if an error
const message = `An error has occured: ${currentWeatherResponse.status}`;
throw new Error(message);
}
const weatherDataResponse = await currentWeatherResponse.json();
// Update state with data
setWeatherData(weatherDataResponse);
}
async function callForcast() {
const key = "";
// Get lat & lon from the previous data fetch
const lon = weatherData.coord.lon
const lat = weatherData.coord.lat
// Get forcast data
const forcastWeatherUrl = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=metric&appid=${key}`
const forcastWeatherResponse = await fetch(forcastWeatherUrl);
if (!forcastWeatherResponse.ok) {
const message = `An error has occured: ${forcastWeatherResponse.status}`;
throw new Error(message);
}
const forcastDataResponse = await forcastWeatherResponse.json();
// Update state with the forcast data
setForcastData(forcastDataResponse);
}
This then runs with the onClick calling both functions
function callWeatherAndForcast() {
callForcast();
callWeather();
}
Upvotes: 2
Views: 1501
Reputation: 28
I think you should try to call callWeather();
under callForcast()
after setForcastData()
state set, and if update state value not affected in call weather you can try to add wait in setForcastData()
.
Or, try to add wait before callForcast()
in callWeatherAndForcast()
onClick
Upvotes: 1
Reputation: 113
Are you using FunctionComponent or Classes ?
Also, keep in mind that updating the state will trigger a rerendering. This means that:
For helping you correctly, I need to know if you use FunctionComponent
or Class
and get the whole Function/Class.
Edit: based on the fact that you're using FunctionComponent.
In order to archive what you want, you need to use hooks
.
Hooks are the way to handle a function component lifecycle.
For your problem, you'll need useState
, useCallback
hooks.
export const DisplayWeather = () => {
const [forecast, setForecast] = useState();
const [weather, setWeather] = useState();
const [error, setError] = useState();
const onSubmit = useCallback(async () => {
getWeather();
getForecast();
}, [forecast, weather]);
const getWeather = useCallback(async () => {
const key = "";
const location = formData.location;
const currentWeatherURL = `https://api.openweathermap.org/data/2.5/weather?q=${location}&units=metric&appid=${key}`;
const apiResponse = await fetch(currentWeatherURL);
if(!apiResponse.ok){
const message = `An error has occured: ${apiResponse.status}`;
setError(message);
} else {
const weatherData = apiResponse.json();
setWeather(weatherData);
}
}, [formData]);
const getForecast = useCallback(async () => {
const key = "";
const lon = weather.coord.lon;
const lat = weather.coord.lat;
const forecastWeatherUrl = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=metric&appid=${key}`
const apiResponse = await fetch(forecastWeatherUrl);
if(!apiResponse.ok) {
const message = `An error has occured: ${apiResponse.status}`;
setError(message);
} else {
const forecastData = apiResponse.json();
setForecast(forecastData);
}
}, [weather]);
if(error){
return (
<p>Error: {error}</p>
)
}
return (
<p>Forecast data</p>
<p>{forecast.data.temperature}</p>
<p>Weather data</p>
<p>{weather.data.temperature}</p>
);
}
In the code above, I set 2 state variables (weather & forecast) and create 3 functions.
The onSubmit
function is called when the user click. His callback depend on two variables (weather & forecast) which are referenced in the dependency array (the []
after the callback)
The getWeather
function is called before getForecast
because the result of the getForecast
function depends on the weather state. That's why you have weather
in the getForecast
callback dependency array. It tells getForecast
that when the value of weather
change, it needs to re-render.
Note that i've added formData
into the dependency array of getWeather
otherwise, when the user click, the getWeather
function won't get any value from formData
.
Note: it is not a working example, just a simple explanation. You can find more infos here: Hooks Reference useCallback Reference
Upvotes: 3
Reputation: 125
State does not update immediately! Meaning that the function I want to get the new state will get the previous state. To fix this I added callForcast function into a useEffect hook which has a dependency on callWeather because callForcast needs callWeather to update state first. This means when this function is run state will be updated in time.
useEffect (() => {
async function callForcast() {
const key = "";
// Get lat & lon from the previous data fetch
const lon = weatherData.coord.lon
const lat = weatherData.coord.lat
// Get forcast data
const forcastWeatherUrl = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=metric&appid=${key}`
const forcastWeatherResponse = await fetch(forcastWeatherUrl);
if (!forcastWeatherResponse.ok) {
const message = `An error has occured: ${forcastWeatherResponse.status}`;
throw new Error(message);
}
const forcastDataResponse = await forcastWeatherResponse.json();
// Update state with the forcast data
setForcastData(forcastDataResponse);
}
// Call the callForcast function to run
callForcast();
},
// This effect hook is dependent on callWeather
[callWeather])
Now my onClick will only need to call callWeather() function.
Thanks to: @Mohammad Arasteh @Thomas Geenen @tromgy
Upvotes: 1
Reputation: 31
use 'await' before calling callForcast so the second function (callWeather) does'nt get called immediately after calling first function.
async function callWeatherAndForcast() {
await callForcast();
callWeather();
}
also as @tromgy mentioned in the comments, React state updates are not immediate, try calling callWeather function inside a hook which has a dependency on forcastData state
Upvotes: 3