React+TypeScript天气网站:如何获取点击城市的ID并调用API
React+TypeScript天气预报站点:获取点击城市ID的解决方案
你的核心问题是**handleClick方法遍历了所有城市ID**,原因是你在点击事件里循环了整个cityInfo数组,而没有直接获取当前点击城市的ID。以下是具体修改方案:
1. 重构handleClick方法
让它接收当前点击城市的ID作为参数,直接更新cityId状态,无需遍历数组:
const handleClick = (targetCityId: number): void => { setCityId(targetCityId); };
2. 修改城市列表渲染逻辑
去掉不必要的<form>标签(此处无需表单提交逻辑),在点击时传入当前城市的ID:
{cityInfo.length > 0 ? cityInfo.map((city, key) => { return ( <div key={key} onClick={() => handleClick(city.id)} style={{ cursor: 'pointer' }}> <h2> {city.name}, {city.state} </h2> </div> ) }) : <p>请输入城市名称搜索</p>}
3. 优化useEffect逻辑(推荐)
原代码中useEffect会在cityName或cityId变化时同时请求城市列表和天气数据,导致切换城市ID时重复请求城市列表。拆分成两个独立的useEffect:
- 仅在
cityName变化时获取城市列表 - 仅在
cityId变化时获取对应城市的天气
// 获取城市列表 useEffect(() => { if (!cityName) return; async function fetchCityList() { const idInfo = await getCityId(cityName); setCityInfo(idInfo); } fetchCityList(); }, [cityName]); // 获取对应城市的天气 useEffect(() => { async function fetchWeather() { const weatherInfo = await getCurrentWeather(cityId.toString()); setCityWeather(weatherInfo); } fetchWeather(); }, [cityId]);
4. 完善类型定义(可选)
避免大量使用any,定义明确的接口约束数据类型:
interface CityInfo { id: number; name: string; state: string; } interface WeatherInfo { // 根据API返回结构补充字段 } // 修改state类型 const [cityInfo, setCityInfo] = useState<CityInfo[]>([]); const [cityWeather, setCityWeather] = useState<WeatherInfo | null>(null); const [cityId, setCityId] = useState<number>(3477);
修改后的完整代码
import { useState, useEffect, ChangeEventHandler, FormEventHandler } from "react" import { ApiAdvisorVariables } from "../../../variables" interface CityInfo { id: number; name: string; state: string; } interface WeatherInfo { // 按需补充API返回的天气字段 } export function ApiAdvisor() { const [cityName, setCityName] = useState<string>('') const [value, setValue] = useState<string>('') const [cityInfo, setCityInfo] = useState<CityInfo[]>([]) const [cityWeather, setCityWeather] = useState<WeatherInfo | null>(null) const [cityId, setCityId] = useState<number>(3477) const handleChange: ChangeEventHandler<HTMLInputElement> = (event) => { setValue(event.target.value) } const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => { event.preventDefault() setCityName(value) } const handleClick = (targetCityId: number): void => { setCityId(targetCityId) } async function getCityId(cityName: string): Promise<CityInfo[]> { const response = await fetch(`http://apiadvisor.climatempo.com.br/api/v1/locale/city?name=${cityName}&token=${ApiAdvisorVariables.token}`) const data = await response.json() return data } async function getCurrentWeather(cityId: string): Promise<WeatherInfo> { const response = await fetch(`http://apiadvisor.climatempo.com.br/api/v1/weather/locale/${cityId}/current?token=${ApiAdvisorVariables.token}`) const data = await response.json() return data } useEffect(() => { if (!cityName) return; async function fetchCityList() { const idInfo = await getCityId(cityName) setCityInfo(idInfo) } fetchCityList() }, [cityName]) useEffect(() => { async function fetchWeather() { const weatherInfo = await getCurrentWeather(cityId.toString()) setCityWeather(weatherInfo) } fetchWeather() }, [cityId]) return ( <section> <div className="search-container"> <form onSubmit={handleSubmit}> <input onChange={handleChange} placeholder="Digite o nome da cidade" value={value} /> <button type="submit">Search</button> </form> </div> <div className="weather-info-container"> {cityInfo.length > 0 ? cityInfo.map((city, key) => { return ( <div key={key} onClick={() => handleClick(city.id)} style={{ cursor: 'pointer' }}> <h2> {city.name}, {city.state} </h2> </div> ) }) : <p>请输入城市名称搜索</p>} </div> {/* 渲染天气详情 */} {cityWeather && ( <div className="weather-details"> {/* 根据WeatherInfo结构渲染具体数据 */} </div> )} </section> ); }
额外优化点:将搜索表单的onClick改为onSubmit,并给输入框绑定value={value},实现受控组件,提升输入状态的可控性。
内容的提问来源于stack exchange,提问作者Alex_Lima84
相关产品推荐
相关产品推荐

