So I have a useEffect hook that has a function called callForcast() this has a dependency of callWeather so when the callWeather() function is run callForcast() runs. callweather() is called using an onClick and the callForcast() I added a call inside the useEffect() but this causes an infinite loop.
How can I make this function run once.
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);
}
// Causing infinite loop
callForcast();
}, [callWeather])
This is what the callForcast function relies on
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);
}
// Get data if no error
const weatherDataResponse = await currentWeatherResponse.json();
// Update state with data
setWeatherData(weatherDataResponse);
}
CodePudding user response:
Why does this have a dependency on callWeather
? It doesn't use callWeather
.
It's executing over and over presumably because whatever callWeather
is, it's changing on each render. (Maybe being re-declared and/or re-assigned on each render.)
If this effect should only execute once, use an empty dependency array:
useEffect(() => {
// the code in the effect
}, []);
This will execute the effect only once when the component first loads.
Alternatively...
when the callWeather() function is run callForcast() runs
If you want to invoke callForcast()
whenever you invoke callWeather()
then useEffect
is the wrong tool. Just define the function you want to call and call it when you want to call it. For example:
const callForcast = () => {
// the code in the function
};
const callWeather = () => {
// the code in the function
callForcast();
};
Another possibility...
If you have a state value that is a dependency and want to invoke the effect any time that dependency changes, use that state value as the dependency:
useEffect(() => {
// the code in the effect
}, [weatherData]);
Or...
If this function just needs some data provided to it from another operation, provide that data. For example:
const callForcast = (dataNeeded) => {
// the code in the function
};
const callWeather = () => {
// the code in the function
callForcast(someData);
};
Overall it's not entirely clear under what circumstances you want to execute callForcast
. (Though you may be clarifying that in the question as I type this.) But in your original code the reason it executes on every render is because callWeather
is changing on every render and is surely the wrong dependency to use.
CodePudding user response:
Try actually calling callWeather
in your effect. and if it is async you can call it within callForcast
useEffect (() => {
// callWeather();
async function callForcast() {
await callWeather();
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);
}
// Causing infinite loop
callForcast();
}, [callWeather])
furthermore you should just have callWeather
return your data in a promise so you can extract it in your callForcast
fn. like const data = await callWeather();
Edit: seeing the updates you probably just want
// will run callForcast every time
useEffect (() => {
async function callForcast() {
...
}
callForcast();
}, [weatherData]);
OR
async function callWeather() {
...
setWeatherData(weatherDataResponse);
return weatherDataResponse;
}
useEffect (() => {
async function callForcast() {
const weatherData = await callWeather();
...
}
callForcast();
}, [callWeather]);