I'm trying to add the user's local weather to my site using openweathermap.org's free API. The API call works; I get want I want back. But then it keeps calling the API hundreds of times. I also am having problems getting the data to render in the browser, which I think is an asynchronous Javascript problem. I have tried moving the function that triggers the fetch call, putting it in a useEffect()
, using a setTimeout()
and nothing works. When it's in the useEffect()
, I get an error pointing to an issue 'reading 'latitude' in the fetch call.
So how do I solve those issues? And is there a way to store the latitude and longitude in a useState()?
FETCH CALL
export const getWeather = (coords, API) => {
return fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${coords.latitude}&lon=${coords.longitude}&units=metric&appid=${API}`)
.then(res => res.json())
}
CODE
export const Weather = () => {
const [weather, setWeather] = useState({})
const APIkey = "(redacted)"
const { coords, isGeolocationAvailable, isGeolocationEnabled } =
useGeolocated({
positionOptions: {
enableHighAccuracy: false,
},
userDecisionTimeout: 5000,
});
getWeather(coords, API).then(data => {setWeather(data)})
return !isGeolocationAvailable ? (
<div>Your browser does not support Geolocation</div>
) : !isGeolocationEnabled ? (
<div>Geolocation is not enabled</div>
) : coords ? (
<div><p>Your weather at {coords.latitude} and {coords.longitude} {weather?.name}, is {Math.round(weather?.main?.temp)}</p>
<p>High/low: {Math.round(weather?.main?.temp_max)}/{Math.round(weather?.main?.temp_min)}</p></div>
) : (
<div>Getting the location data… </div>
);
};```
CodePudding user response:
That happens because in JSX, all of the code gets executed during each re-render. So each time your component re-render, the getWeather(...)
will be called once, which is kind of scary.
useEffect
is the right tool to use for things like on-mount API call, I'm not sure what "'reading 'latitude' in the fetch call" really means, maybe you can try this:
useEffect(() => {
if (coords && isGeolocationAvailable && isGeolocationEnabled) {
getWeather(coords, APIkey).then(setWeather)
}
}, [coords, isGeolocationAvailable, isGeolocationEnabled])
Notes:
- If
APIkey
is sensitive data, you probably shouldn't put it right in your component. Try using dotenv or something else to store those variables, depending on your development environment. - The code in
useEffect
will be executed every time when any of its dependencies changes. So if you only want it to run once, either carefully select what should be put into dependency list, or use a boolean flag to prevent it from calling more than one time.