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

不使用状态管理库,跨组件传递location至WeatherApi的fetch请求方法

解决方案:不使用状态管理库传递location并触发API查询

原代码核心问题

  • 把WeatherApi当成普通函数调用,违反React组件/Hook规范,导致每次渲染都重建状态、无法持久化数据
  • useEffect依赖数组为空,不会响应location变化
  • 未区分「输入地点」和「触发查询」动作,易引发不必要的API请求

正确实现:将WeatherApi改为自定义Hook

自定义Hook是React官方推荐的状态逻辑复用方式,无需状态管理库即可在组件间共享逻辑,同时保持状态与组件绑定。

1. 修改api.js为自定义Hook

import { API_KEY, BASE_URL } from "./keys";
import { useEffect, useState } from "react";

// 自定义Hook必须以use开头
export const useWeatherApi = ({ location, shouldFetch }) => {
  const [apiData, setApiData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    // 仅当触发信号为真且地点不为空时发起请求
    if (!shouldFetch || !location.trim()) return;

    const fetchWeather = async () => {
      setLoading(true);
      setError(null);
      try {
        const res = await fetch(`${BASE_URL}weather?q=${location}&appid=${API_KEY}`);
        const data = await res.json();
        setApiData(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchWeather();
  }, [location, shouldFetch]); // 依赖变化时重新请求

  return { apiData, loading, error };
};

2. 修改InputsAndBtn.js使用自定义Hook

import { useState, useEffect } from "react"
import './App.css';
import { useWeatherApi } from "./api";

const InputsAndBtn = () => {
    const [location, setLocation] = useState("")
    const [fetchTrigger, setFetchTrigger] = useState(false); // 控制请求触发的开关
    const { apiData, loading, error } = useWeatherApi({ location, shouldFetch: fetchTrigger });
    const [clouds, setClouds] = useState(null)
    const [description, setDescription] = useState(null)

    function handleChange(event) {
        setLocation(event.target.value)
        // 输入时重置旧结果
        setClouds(null);
        setDescription(null);
    }

    function handleBtn(event) {
        event.preventDefault()
        // 点击按钮翻转触发状态,触发API请求
        setFetchTrigger(prev => !prev);
    }

    // API返回数据后自动提取所需字段
    useEffect(() => {
        if (apiData && apiData.name === location) {
            setClouds(apiData.clouds?.all || null);
            setDescription(apiData.weather?.[0]?.description || null);
        }
    }, [apiData, location])

    return (
        <form className="form">
            <div style={{display: "flex"}}>
                <section style={{marginRight: "auto"}}>
                    <input 
                        onChange={handleChange}
                        type="text"
                        placeholder="输入城市名称"
                        value={location}
                    />
                    <button onClick={handleBtn} disabled={!location.trim() || loading}>
                        {loading ? "查询中..." : "开始查询"}
                    </button>
                    {error && <p style={{color: "red"}}>查询失败:{error}</p>}
                </section>
                {clouds && <section style={{backgroundColor: "red", width: "30vw", height: "100vh"}}>
                    <h1>云量:{clouds}%</h1>
                    <h2>天气描述:{description}</h2>
                </section>}
            </div>
        </form>
    )
}

export default InputsAndBtn

关键优化点

  • Hook规范:遵循React自定义Hook命名规则,确保状态和副作用被正确管理
  • 请求控制:用fetchTrigger状态控制请求时机,避免输入过程中频繁调用API
  • 用户体验:增加加载状态、错误提示,处理API返回数据为空的边界情况
  • 依赖正确:useEffect依赖location和fetchTrigger,确保仅在必要时重新请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 02:23:13