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

React调用Foreca Weather API获取天气数据的问题排查

问题:Foreca Weather API调用异常排查与优化建议

我尝试通过Foreca Weather API获取天气数据,获取当前天气需要传入对应城市的数字型location参数,这个参数得通过地点搜索接口获取。我写了searchCity函数,先获取location id再传入weatherUrl请求当前天气,但首次搜索时能拿到locations数组却无法获取天气,Axios返回400错误;第二次搜索两者都能获取,但天气数据是上一次请求的结果。附上我的代码:

import React from 'react'
import { useState } from 'react';
import axios from 'axios';
const Home = () => {
  // const [weather, setWeather] = useState({});
  const [city, setCity] = useState('');
  const [location, setLocation] = useState([""]);
  const [weather, setWeather] = useState({});


  const url = `https://foreca-weather.p.rapidapi.com/location/search/${city}`
  let weatherUrl;
  const options = {
    headers: {
      'X-RapidAPI-Key': 'apiKeyHere',
      'X-RapidAPI-Host': 'foreca-weather.p.rapidapi.com'
    }
  };
  const weatherOptions = {
    params: { alt: '0', tempunit: 'C', windunit: 'MS', tz: 'Europe/London', lang: 'en' },
    headers: {
      'X-RapidAPI-Key': 'apiKeyHere',
      'X-RapidAPI-Host': 'foreca-weather.p.rapidapi.com'
    }
  };

  const searchCity = async (e) => {
    try {
      if (e.key === 'Enter') {
        await axios.get(url, options).then((response) => {
          setLocation(response.data.locations);
          console.log(response.data);
          weatherUrl = `https://foreca-weather.p.rapidapi.com/current/${location[0]?.id}`;
        })
        setCity('')
        // To show the current weather of the city searched
        currentWeather(); 


      }
    } catch (err) {
      console.error(err);
    }
  }

  const currentWeather = async () => {
    try {
      await axios.get(weatherUrl, weatherOptions).then((response) => {
        setWeather(response.data);
        console.log(response.data);
      })
    } catch (err) {
      console.error(err);
    }

  }
  return (
    <> <div>
        <div className="search">
          <input
            value={city}
            onChange={e => setCity(e.target.value)}
            onKeyPress={searchCity}
            type="text" />
        </div>

        <div className="weather">
          <h1>City:{location[0]?.name}</h1>
          <h1>Country:{location[0]?.country}</h1>
          <h1>id:{location[0]?.id}</h1>
          <h1>weather:{weather.current?.temperature}</h1>
        </div>
      </div>
    </>

  )
}

export default Home;

问题原因分析

  • React状态更新的异步性:setLocation是异步操作,在then回调里调用setLocation后,立刻访问location[0]?.id拿到的还是组件当前的旧状态值。首次搜索时,weatherUrl用了初始空数组的无效id,导致API返回400错误;第二次搜索时,location存储的是上一次的结果,所以请求用了旧id,返回旧数据。
  • 变量作用域与时序问题:顶层的weatherUrl变量会被多次赋值覆盖,再加上异步状态更新的延迟,进一步导致请求地址错误。

优化实现方案

直接绕开异步状态的依赖,用接口返回的原始数据发起天气请求,同时合并逻辑、增加错误处理:

import React, { useState } from 'react';
import axios from 'axios';

const Home = () => {
  const [city, setCity] = useState('');
  const [location, setLocation] = useState([]);
  const [weather, setWeather] = useState({});
  const [errorMsg, setErrorMsg] = useState('');

  // 提取重复的请求头,减少冗余代码
  const baseHeaders = {
    'X-RapidAPI-Key': 'apiKeyHere',
    'X-RapidAPI-Host': 'foreca-weather.p.rapidapi.com'
  };

  const searchCity = async (e) => {
    if (e.key !== 'Enter') return;
    if (!city.trim()) {
      setErrorMsg('请输入城市名称');
      return;
    }

    try {
      setErrorMsg('');
      // 1. 先请求城市搜索接口
      const locationRes = await axios.get(
        `https://foreca-weather.p.rapidapi.com/location/search/${city}`,
        { headers: baseHeaders }
      );

      const locations = locationRes.data.locations;
      if (!locations || locations.length === 0) {
        setErrorMsg('未找到匹配的城市');
        return;
      }
      setLocation(locations);

      // 2. 直接用接口返回的最新location id请求天气,不用等state更新
      const weatherRes = await axios.get(
        `https://foreca-weather.p.rapidapi.com/current/${locations[0].id}`,
        {
          params: { alt: '0', tempunit: 'C', windunit: 'MS', tz: 'Europe/London', lang: 'en' },
          headers: baseHeaders
        }
      );

      setWeather(weatherRes.data);
      setCity('');
    } catch (err) {
      console.error(err);
      setErrorMsg('获取数据失败,请稍后重试');
    }
  };

  return (
    <div>
      <div className="search">
        <input
          value={city}
          onChange={e => setCity(e.target.value)}
          onKeyPress={searchCity}
          type="text"
          placeholder="输入城市名称按回车搜索"
        />
        {errorMsg && <p style={{ color: 'red', margin: '8px 0' }}>{errorMsg}</p>}
      </div>

      <div className="weather">
        {location[0] && (
          <>
            <h1>城市: {location[0].name}</h1>
            <h1>国家: {location[0].country}</h1>
            <h1>ID: {location[0].id}</h1>
          </>
        )}
        {weather.current && (
          <h1>气温: {weather.current.temperature}°C</h1>
        )}
      </div>
    </div>
  );
};

export default Home;

优化点说明

  1. 直接使用接口返回的locations数据中的id发起天气请求,彻底避免React异步状态更新带来的时序问题
  2. 合并两个异步请求的逻辑,代码更连贯,减少拆分函数导致的冗余
  3. 提取重复的请求头配置,降低代码维护成本
  4. 增加空输入校验、无搜索结果提示、请求失败提示,提升用户体验
  5. 优化条件渲染,避免无效的空值展示

内容的提问来源于stack exchange,提问作者Jarv1s

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 06:10:34