You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 12:39:25