React中Fetch拼接模板字符串URL偶发失效返回空数组
问题根因
你的代码存在三个核心问题,直接对应你遇到的异常现象:
- 请求触发时机混乱:
useEffect传入空依赖数组,只会在组件首次挂载时执行1次;同时你在effect外部定义的getWeather是每次组件重渲染都会重新生成的函数,空依赖捕获的是首次渲染时的函数闭包,属于典型的React闭包陷阱,才会出现只有意外重渲染才偶尔发起请求的情况。 - 初始值类型不匹配:
weatherData初始值设为空数组,而OpenWeather天气接口返回的是对象结构,首次渲染时子组件拿到的是空数组展开后的空属性,看起来就像没有返回数据。 - 请求错误无感知:你的代码没有校验接口响应状态,加上如果请求地址里的参数分隔符误写为HTML转义字符
&,会导致接口参数解析失败,请求出错时只会静默打印控制台日志,不会更新页面状态,进一步放大了“返回空数组”的错觉。
修复方案
推荐把请求逻辑收拢到useEffect内部,从根源规避闭包问题,同时补全状态兜底逻辑,修复后代码如下:
import './App.css'; import Daily from './components/daily'; import {useState, useEffect} from 'react' function App() { const [city, setCity] = useState('London') // 初始值设为null,方便区分初始加载状态和接口返回状态 const [weatherData, setWeather] = useState(null) const [loading, setLoading] = useState(true) const [errorMsg, setErrorMsg] = useState('') useEffect(()=>{ const getWeather = async() =>{ try{ setLoading(true) // 注意参数分隔符用&,不要用HTML转义的& const resp = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=f8d957da06af592a1390c360ea801908&units=metric`) // 先校验HTTP响应状态,再解析JSON if (!resp.ok) throw new Error('天气数据请求失败,请检查配置') const data = await resp.json() setWeather(data) setErrorMsg('') }catch(err){ setErrorMsg(err.message) console.error('请求异常:', err) }finally{ setLoading(false) } } getWeather() // 把city加入依赖数组,首次挂载、city变更时都会自动触发请求 }, [city]) // 加载和错误状态兜底 if (loading) return <div>正在加载天气数据...</div> if (errorMsg) return <div>加载失败:{errorMsg}</div> return( <> {weatherData && <Daily {...weatherData} />} </> ) } export default App;
关键修改说明
- 将异步请求函数移到
useEffect内部,消除跨作用域的闭包引用问题 - 给
useEffect添加city作为依赖项,保证请求触发时机符合预期,不会出现偶发不执行的问题 - 修正请求地址的参数分隔符,新增HTTP响应状态校验,避免接口返回错误时静默失败
- 新增加载、错误状态兜底,调整
weatherData初始值类型匹配接口返回结构,避免首次渲染传入无效空数据 - 增加数据存在性判断,确认接口返回有效数据后再传入子组件渲染
如果你后续需要在effect外部手动触发请求刷新,可以用
useCallback包裹getWeather函数后再加入useEffect的依赖数组,不要在空依赖的effect里直接引用外部定义的普通函数,否则很容易再次触发闭包陷阱。
内容的提问来源于stack exchange,提问作者Het Patel
相关产品推荐
相关产品推荐

