不使用状态管理库,跨组件传递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
相关产品推荐
相关产品推荐

