I'm trying to create a Weather app to practice React and have a few issues. You can find my code here: Codesandbox
I have 3 components:
Weather.jsx
I'm using axios to pull data from the openweather API. The default city is set to "New York". Passing {data, city, set city, handleClick} as props to the Form.jsx.
const Weather = () => {
const [data, geWeatherData] = useState(undefined);
const [city, setCity] = useState("New York");
const [click, handleClick] = useState(false);
useEffect(() => {
if (!click) {
const getWeather = async () => {
city &&
(await weatherData(city).then((response) => {
geWeatherData(response.data);
// console.log(response.data);
console.log(response.data.main.temp);
}));
};
getWeather();
handleClick(true);
}
}, [click, city]);
const classes = useStyles();
return (
<Box className={classes.component}>
<Box className={classes.weatherContainer}>
<Form
data={data}
city={city}
setCity={setCity}
handleClick={handleClick}
/>
</Box>
</Box>
);
};
```
**Form.jsx**
I'm planning to use this component to design the input but also get the city from the user.I'm also passing data to WeatherDetail component so that I can show content. Ideally, I should make this into a separate one, I decided to just club them together.
```
const Form = ({ city, setCity, handleClick, data }) => {
const classes = useStyles();
const handleCityChange = (value) => {
setCity(value);
};
return (
<>
<Box>
<TextField
inputProps={{ className: classes.input }}
value={city}
autoFocus
className={classes.input}
onKeyPress={(e) => {
if (e.key === "Enter") {
handleClick(true);
}
}}
onChange={(e) => handleCityChange(e.target.value)}
label="Enter any City in USA"
variant="standard"
/>
</Box>
<Weatherdetail data={data} />
</>
);
};
export default Form;
WeatherDetail.jsx
Passing the city and data from the API to construct the visual details of city weather.
const WeatherDetail = ({ data }) => {
const classes = useStyles();
return data ? (
<>
<Box className={classes.temp} component="div">
{data.main.temp}
<Box style={{ fontSize: "10px" }} component="span">
Fahranheit
</Box>
</Box>
</>
) : (
<Box>
<p>Lets do something</p>
</Box>
);
};
export default WeatherDetail;
Things that are not working out for me:
<Box className={data.main.temp < 40 ?classes.componentFog : classes.component}>
<Box className={classes.weatherContainer}>
<Form
data={data}
city={city}
setCity={setCity}
handleClick={handleClick}
/>
</Box>
</Box>
But data does not seem to pass.
I tried speaking to a few dev friends but could not resolve the issue. Thank you in advance.
You only set the click
to true once, it's never toggled back to false
so additional queries can be made.
You also need to handle failed weather requests.
Form - When the TextField
is interacted with you should reset the click
state.
const Form = ({ city, setCity, handleClick, data }) => {
const classes = useStyles();
const handleCityChange = (value) => {
handleClick(false); // <-- onChange reset the click state
setCity(value);
};
return (
<>
<Box>
<TextField
inputProps={{ className: classes.input }}
value={city}
autoFocus
className={classes.input}
onKeyPress={(e) => {
if (e.key === "Enter") {
handleClick(true);
}
}}
onChange={(e) => handleCityChange(e.target.value)}
label="Enter any City in USA"
variant="standard"
/>
</Box>
<Weatherdetail data={data} />
</>
);
};
Weather - Catch rejected Promises from the axios requests/weatherData service.
useEffect(() => {
if (!click) {
const getWeather = async () => {
city &&
(await weatherData(city).then((response) => {
geWeatherData(response.data);
console.log(response.data.main.temp);
}).catch(error => {
// log/show error message/etc...
}));
};
getWeather();
handleClick(true);
}
}, [click, city]);
Since the data
state is potentially undefined you should handle conditionally rendering the class using Optional Chaining operator.
className={data?.main?.temp < 40 ? classes.componentFog : classes.component}