Reputation: 451
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.
Upvotes: 1
Views: 68
Reputation: 3123
Here is a forked example to make search work:
https://codesandbox.io/s/newweather-dec-forked-113re
Upvotes: 1
Reputation: 1781
Sorry if I missed something.. but I have made a simple change and it seems to work for me. I am new to React myself, just started getting into it last night. But here is the code:
const Weather = () => {
const [data, geWeatherData] = useState(undefined);
const [city, setCity] = useState("New York");
const [click, handleClick] = useState(false); // not sure why this is used
const updateApi = () => {
const getWeather = async () => {
city &&
(await weatherData(city).then((response) => {
geWeatherData(response.data);
console.log(response.data.main.temp);
}));
};
getWeather();
}
// 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={updateApi} // Altered line here
/>
</Box>
</Box>
);
};
export default Weather;
I figured that you search the City via key press (Enter). So I thought it's better to call a function (updateApi) when Enter is pressed.
Regarding the background change, I see Drew Reese
has got the answer for you.
Upvotes: 1
Reputation: 202618
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}
Upvotes: 1