I am working on a simple weather app that use OpenWeatherAPI to get the weather forcast for the day and display it. I have gotten the app to a fnuctioning point and now I want to work on optimizing the code. In order to get this application to work I am using the code below:
function App() {
const [zipCodeData, setZipCodeData] = useState('');
const [isValidZip, setIsValidZip] = useState(false);
const handleZipCodeData = zipCodeData => {
setZipCodeData(zipCodeData);
setIsValidZip(true);
};
const geoLocationData = ConvertZipCode(zipCodeData, api);
console.log(geoLocationData);
console.log(`${zipCodeData} from parent component`);
return (
<ChakraProvider>
<Grid templateColumns="repeat(5, 1fr)" templateRows="repeat(4, 1fr)">
{!isValidZip ? (
<ZipCodeForm sendZipCodeData={handleZipCodeData} />
) : (
<WeatherBox geoLocationData={geoLocationData} />
)}
</Grid>
</ChakraProvider>
);
}
export default App;
I use the custom hook ConvertZipcode to make an API call that allows me to get Latitude and Longitude values for a zipcode that I then use to get the weather data. Here is a look at my custom hook:
const ConvertZipCode = (zipCodeData, api) => {
const [geoInformation, setGeoInformation] = useState({
longitude: '',
latitude: '',
zip: '',
cityName: '',
});
useEffect(() => {
const converZip = async () => {
const result = await fetch(
`http://api.openweathermap.org/geo/1.0/zip?zip=${zipCodeData}&appid=${api}`
);
if (!result.ok) {
throw new Error('Could not perfom the zip to geo location converter. ');
}
const data = await result.json();
const { cityName, latitude, longitude } = {
cityName: data.name,
latitude: data.lat,
longitude: data.lon,
};
setGeoInformation({
cityName,
latitude,
longitude,
});
};
converZip();
}, [zipCodeData, api]);
return geoInformation;
};
export default ConvertZipCode;
When this code runs, it makes the api call before the zip code data is inputed by the user. I wanted to use the useEffect hook in order to stop this initial api call. I ran the code below:
function App() {
const [zipCodeData, setZipCodeData] = useState('');
const [isValidZip, setIsValidZip] = useState(false);
const [geoLocationData, setGeoLocationData] = useState({});
const handleZipCodeData = zipCodeData => {
setZipCodeData(zipCodeData);
setIsValidZip(true);
};
useEffect(() => {
if (isValidZip && zipCodeData) {
// Make the API call only if the zip code is valid and available
ConvertZipCode(zipCodeData, api).then(data => {
setGeoLocationData(data);
});
}
}, [isValidZip, zipCodeData]);
return (
<ChakraProvider>
<Grid templateColumns="repeat(5, 1fr)" templateRows="repeat(4, 1fr)">
{!isValidZip ? (
<ZipCodeForm sendZipCodeData={handleZipCodeData} />
) : (
<WeatherBox geoLocationData={geoLocationData} />
)}
</Grid>
</ChakraProvider>
);
}
export default App;
When I run this code, I get the following error:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- You might have more than one copy of React in the same app
I have double check and it seems like I am breaking the rules of Hooks. Can anyone point me in the right direction? Am I misusing useEffect?
Any insight would be amazing. Thank you in advanced.
I want to fix the unnecessary API calls at the start of the applicaitons rendering.
2
Answers
check this link, this will solve your issue https://react.dev/learn/reusing-logic-with-custom-hooks
The actual role of
ConvertZipCode
is nothing more than making an API call, and it has no reason to promote it as hook.The state is already present in the
App
section, and it has no reason to be duplicated. It yields no benefits, but lot of troubles.So keep it as a plain async function:
Please, note that I renamed the function to the standard camel-casing for JavaScript. A formal point, but rather important for React.
Then, modify the
App
accordingly:As final point, your
geoLocationData
structure defines also thezip
field, but that’s not fed by the function.