React: отрисовка элемента при валидном значении input
Есть приложение, которое отображает погоду в локации, которую пользователь вписывает в input, каждый раз создавая блок-карточку с названием города, погодными данными и т.д. Все данные берутся из апишки openweathermap.org. Сама логика работает, но не учитываются ошибки, вроде неверно введенной локации. Пробовал несколько способов, но все они мне кажутся костыльными. Как правильнее всего блокировать отрисовку блока при неверно введенном городе и обнулить api после самой отрисовки(чтобы информация не тянулась даже при пустом input)?
App.js
import React, { useEffect, useState } from "react";
import axios from "axios";
import {BsCloudHaze2Fill, BsCloudDrizzleFill, BsEye, BsWater, BsThermometer, BsWind, BsFillThunderboltFill} from 'react-icons/bs'
import {IoSunny, IoRainy, IoCloudy, IoSnow, IoThunderstorm} from 'react-icons/io5'
import {TbTemperatureCelsius, TbMist} from 'react-icons/tb'
import {ImSpinner8} from 'react-icons/im'
import './scss/style.scss';
import Form from "./components/form";
import Weather from "./components/weather";
function App() {
const [data, setData] = useState(null);
const [location, setLocation] = useState('Moscow');
const [error, setError] = useState('');
const [listItem, setListItem] = useState([])
useEffect(()=> {
const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${location}&units=metric&appid=ff5bb4c4a9717f82bb05d858d81d8eb0&lang=ru`;
axios.get(apiUrl).then((response) => {
setData(response.data);
}).catch(err=>{
setError(err);
});
}, [location]);
useEffect(()=> {
const timer = setTimeout(() => {
setError('');
}, 2000)
return () => clearTimeout(timer);
}, [error])
if (!data) {
return <div>
<div className="flex flex_center spin">
<ImSpinner8 className="animate-spin"/>
</div>
</div>
}
let icon;
switch (data.weather[0].main) {
case 'Clear':
icon = <IoSunny className="color-yellow" />
break
case 'Clouds':
icon = <IoCloudy />
break
case 'Snow':
icon = <IoSnow className="color-blue" />
break
case 'Rain':
icon = <IoRainy className="color-blue" />
break
case 'Drizzle':
icon = <BsCloudDrizzleFill className="color-dark-blue" />
break
case 'Thunderstorm':
icon = <IoThunderstorm className="color-dark-blue" />
break
case 'Mist':
icon = <TbMist className="color-gray" />
break
}
const date = new Date();
return (
<div className="app">
<div className="container">
<Form
data={data}
error={error}
listItem={listItem}
setListItem={setListItem}
setLocation={setLocation}
/>
</div>
<div className="container">
<div className="flex flex_center app_block">
<Weather listItem={listItem} setListItem={setListItem} />
</div>
</div>
</div>
);
};
export default App;
Form.jsx
import React, { useState } from "react";
import { v4 as uuidv4 } from 'uuid';
import { IoSearch } from 'react-icons/io5'
function Form({ error, listItem, setListItem, setLocation }) {
const [inputValue, setInputValue] = useState('');
const handInput = (e) => {
setInputValue(e.target.value);
};
const handSubmit = (e) => {
if (inputValue !== '') {
setLocation(inputValue);
setListItem(
[...listItem, {
id: uuidv4(),
location: inputValue
}]
)
}
document.querySelector('input').value = '';
e.preventDefault();
};
return (
<div>
<div className="app_error">{error && <div>Ошибка, веденный вами город не найден!</div>}</div>
<form className="app_form">
<input onChange={(e)=> handInput(e)} type="text" name="city" className="app_form_input" placeholder="Введите город" />
<button onClick={(e)=> handSubmit(e)} className="flex app_form_button"><IoSearch /></button>
</form>
</div>
);
}
export default Form;
weather.jsx
import React from "react";
import {TbTemperatureCelsius} from 'react-icons/tb'
function Weather({ error, listItem, setListItem }) {
function deleteItem(id) {
let newListItem = [...listItem].filter(listItem => listItem.id !== id);
setListItem(newListItem);
}
return (
<div>
{
listItem.map( item => (
<div id={item.id} key={item.id}>
<div>{ item.location }</div>
<button onClick={() => deleteItem(item.id)}>X</button>
</div>
))
}
</div>
);
}
export default Weather;