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

React天气应用如何通过同一表单提交触发双API请求并管理状态

问题原因

你现在的代码有两个核心问题导致功能无法正常联动:

  • 状态混用:把「用户输入的城市名称」和「接口返回的城市图片URL」存在同一个city状态中,图片请求成功后会直接把原本存储的城市名覆盖为图片链接,后续天气请求无法拿到正确的城市查询参数
  • 提交逻辑拆分:表单提交事件、按钮点击事件分别只绑定了单个接口请求函数,触发提交时只会执行其中一个请求,无法同时拉取两类数据;且按钮重复绑定提交事件还可能造成重复请求问题
修复步骤
  1. 拆分独立状态,按数据用途分开存储,避免互相覆盖
    • 用searchCity存储用户在输入框输入的城市名称
    • 用bgImageUrl存储Unsplash接口返回的城市背景图地址
    • 用weatherInfo存储天气接口返回的温度、风速等详情数据,初始值设为null
  2. 写统一的表单提交处理函数,提交时并行触发两个接口请求(两个接口无前后依赖,用Promise.all并行请求可以缩短加载等待时间)
  3. 移除搜索按钮上多余的onClick绑定,只保留表单的onSubmit事件绑定统一处理函数即可,按钮设置type="submit"会自动触发表单提交
  4. 页面底部预留区域渲染weatherInfo里存储的天气数据即可
修复后完整代码
import { useState } from 'react';
import axios from 'axios';
import { BsSearch } from 'react-icons/bs';

function App() {
  // 拆分三个独立状态,各司其职
  const [searchCity, setSearchCity] = useState("");
  const [bgImageUrl, setBgImageUrl] = useState("");
  const [weatherInfo, setWeatherInfo] = useState(null);
  const [loading, setLoading] = useState(false);

  // 统一提交处理函数
  const handleSearchSubmit = async (e) => {
    e.preventDefault();
    if (!searchCity.trim()) return;
    setLoading(true);

    try {
      // 并行发起两个接口请求,减少等待时间
      const [imageRes, weatherRes] = await Promise.all([
        axios.get(`https://api.unsplash.com/photos/random?query=${searchCity}&client_id={API_KEY}`),
        axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${searchCity}&units=metric&appid=${OTHER_API_KEY}`)
      ]);

      // 分别更新对应状态
      setBgImageUrl(imageRes.data.urls.regular);
      setWeatherInfo(weatherRes.data);
    } catch (err) {
      console.error("请求失败:", err);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="App">
      {/* HEADER */}
      <div className="bg-black w-full h-[600px] relative">
        <img
          src={bgImageUrl}
          alt="city image"
          className="w-full h-[600px] object-cover"
        />
        {/* Search and Info */}
        <div className="absolute w-full h-[600px] top-[0%] flex flex-col justify-center bg-gradient-to-t from-black">
          <div className="flex justify-center py-4">
            <h1 className="font-bold text-white text-3xl">Weather App</h1>
          </div>

          <div className="flex justify-center">
            <form
              onSubmit={handleSearchSubmit}
              className="flex justify-between border border-white rounded-xl max-w-[300px] p-4 w-full"
            >
              <input
                onChange={(e) => setSearchCity(e.target.value)}
                value={searchCity}
                className="bg-transparent focus:outline-none placeholder:text-white text-white w-full mr-2"
                type="text"
                placeholder="Search a City..."
                disabled={loading}
              />
              <button type="submit" disabled={loading} className="text-white">
                <BsSearch size={20} />
              </button>
            </form>
          </div>
        </div>
      </div>

      {/* 天气数据渲染区域 */}
      <div className="p-6 max-w-4xl mx-auto">
        {loading && <p className="text-center">加载中...</p>}
        {weatherInfo && !loading && (
          <div className="grid grid-cols-2 md:grid-cols-4 gap-4 mt-6">
            <div className="p-4 border rounded-lg">
              <p className="text-gray-500">城市</p>
              <p className="text-2xl font-bold">{weatherInfo.name}</p>
            </div>
            <div className="p-4 border rounded-lg">
              <p className="text-gray-500">温度</p>
              <p className="text-2xl font-bold">{Math.round(weatherInfo.main.temp)}°C</p>
            </div>
            <div className="p-4 border rounded-lg">
              <p className="text-gray-500">风速</p>
              <p className="text-2xl font-bold">{weatherInfo.wind.speed} m/s</p>
            </div>
            <div className="p-4 border rounded-lg">
              <p className="text-gray-500">天气</p>
              <p className="text-2xl font-bold">{weatherInfo.weather[0].main}</p>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

export default App;
额外优化点
  • 给输入框加了受控绑定value={searchCity},避免输入值和状态不同步
  • 增加了loading状态,请求过程中禁用输入框和按钮,避免重复提交
  • 加了空输入校验,用户没输入内容时不会发无效请求
  • 加了基础的错误捕获,接口报错时不会直接导致页面崩溃
  • 补了天气数据的基础渲染示例,你可以根据接口返回的字段自行调整展示样式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:48:22