React天气应用报错TypeError: forcastWeather.list is undefined求助
天气应用API调用后页面空白问题排查与修复
问题描述
我是JavaScript和React新手,正在开发一款兼具当前天气与未来天气预报功能的应用。首次保存页面时,两次API调用均可正常展示数据,但切换页面或刷新后再次操作,页面会变为空白。在Firefox中检测到报错:Uncaught TypeError: forcastWeather.list is undefined。相关代码如下:
import {React, useState} from "react" import './weather.css' const Weather = () => { const apiKey = 'obsured-so-that-no-one-uses-my-key' const [weatherData, setWeatherData] = useState([{}]) const [city, setCity] = useState("") const [forcastWeather, setForcastWeather] = useState([{}]) const getWeather = (event) => { if (event.key === "Enter") { {fetch(`http://api.openweathermap.org/data/2.5/forecast?q=${city}&appid=${apiKey}&units=metric`) .then(response => response.json()).then(data => {setForcastWeather(data)})} {fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`) .then(response => response.json()).then(data => {setWeatherData(data)})} } } return ( <> <div className="container-about-weather"> <input className="input-about-weather" placeholder="Search" onChange={e => setCity(e.target.value)} value={city} onKeyPress={getWeather}> </input> <div> {typeof weatherData.main === 'undefined' ? ( <div className="weather-content-rows-beginning"> <p>Welcome to my weather app. Enter a city name to get the weather for that city </p> </div> ) : ( <div className="weather-all"> <div className="weather-now"> <div className="weather-content-rows-name"> <p>{weatherData.name}, {weatherData.sys.country}</p> </div> <div className="weather-content-rows-temp"> <p>{Math.round(weatherData.main.temp)}°C</p> <img src={`https://openweathermap.org/img/wn/${weatherData.weather[0].icon}@2x.png`} alt=""/> </div> <div className="weather-content-rows-description"> <p>{weatherData.weather[0].description}</p> </div> <div className="weather-content-humidity"> <p>Humidity: {weatherData.main.humidity}</p> </div> <div className="weather-content-wind"> <p>Wind: {(weatherData.wind.speed * 2.23694).toFixed(1)} mph</p> </div> </div> <div className="weather-forcast"> <div className="weather-forcast-days"> <img src={`https://openweathermap.org/img/wn/${forcastWeather.list[5].weather[0].icon}@2x.png`}></img> <p className="weather-forcast-days-text">{forcastWeather.list[5].main.temp}</p> </div> </div> </div> )} </div> </div> </> ) } export default Weather
错误原因分析
- 初始状态类型不匹配:
forcastWeather和weatherData初始值设为[{}](数组),但OpenWeatherMap API返回的是单个对象,初始状态下forcastWeather.list不存在,直接访问forcastWeather.list[5]会触发TypeError。 - 状态判断逻辑缺失:仅判断
weatherData.main是否存在,未同步检查forcastWeather.list的加载状态。当weatherData加载完成但forcastWeather未返回数据时,会直接渲染天气预报部分导致报错。 - 混合内容拦截:天气预报API使用
http协议,若页面在https环境下运行,浏览器会拦截该请求,导致forcastWeather无法获取数据,始终保持初始状态。
修复方案
1. 修正初始状态类型
将weatherData和forcastWeather的初始值改为空对象{},匹配API返回的数据结构:
const [weatherData, setWeatherData] = useState({}) const [forcastWeather, setForcastWeather] = useState({})
2. 完善状态判断逻辑
在渲染天气内容前,同时检查两个API的返回数据是否就绪:
{typeof weatherData.main === 'undefined' || typeof forcastWeather.list === 'undefined' ? ( <div className="weather-content-rows-beginning"> <p>Welcome to my weather app. Enter a city name to get the weather for that city</p> </div> ) : ( // 渲染天气内容 )}
3. 统一API请求协议
将天气预报API的URL改为https,避免混合内容拦截:
fetch(`https://api.openweathermap.org/data/2.5/forecast?q=${city}&appid=${apiKey}&units=metric`)
4. 可选:添加错误捕获处理
捕获API请求失败的情况,避免异常数据导致渲染错误:
const getWeather = (event) => { if (event.key === "Enter") { fetch(`https://api.openweathermap.org/data/2.5/forecast?q=${city}&appid=${apiKey}&units=metric`) .then(response => { if (!response.ok) throw new Error('Forecast request failed'); return response.json(); }) .then(data => setForcastWeather(data)) .catch(err => console.error(err)); fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`) .then(response => { if (!response.ok) throw new Error('Current weather request failed'); return response.json(); }) .then(data => setWeatherData(data)) .catch(err => console.error(err)); } }
修改后的完整代码
import {React, useState} from "react" import './weather.css' const Weather = () => { const apiKey = 'obsured-so-that-no-one-uses-my-key' const [weatherData, setWeatherData] = useState({}) const [city, setCity] = useState("") const [forcastWeather, setForcastWeather] = useState({}) const getWeather = (event) => { if (event.key === "Enter") { fetch(`https://api.openweathermap.org/data/2.5/forecast?q=${city}&appid=${apiKey}&units=metric`) .then(response => { if (!response.ok) throw new Error('Forecast request failed'); return response.json(); }) .then(data => setForcastWeather(data)) .catch(err => console.error(err)); fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`) .then(response => { if (!response.ok) throw new Error('Current weather request failed'); return response.json(); }) .then(data => setWeatherData(data)) .catch(err => console.error(err)); } } return ( <> <div className="container-about-weather"> <input className="input-about-weather" placeholder="Search" onChange={e => setCity(e.target.value)} value={city} onKeyPress={getWeather} /> <div> {typeof weatherData.main === 'undefined' || typeof forcastWeather.list === 'undefined' ? ( <div className="weather-content-rows-beginning"> <p>Welcome to my weather app. Enter a city name to get the weather for that city</p> </div> ) : ( <div className="weather-all"> <div className="weather-now"> <div className="weather-content-rows-name"> <p>{weatherData.name}, {weatherData.sys.country}</p> </div> <div className="weather-content-rows-temp"> <p>{Math.round(weatherData.main.temp)}°C</p> <img src={`https://openweathermap.org/img/wn/${weatherData.weather[0].icon}@2x.png`} alt={weatherData.weather[0].description} /> </div> <div className="weather-content-rows-description"> <p>{weatherData.weather[0].description}</p> </div> <div className="weather-content-humidity"> <p>Humidity: {weatherData.main.humidity}%</p> </div> <div className="weather-content-wind"> <p>Wind: {(weatherData.wind.speed * 2.23694).toFixed(1)} mph</p> </div> </div> <div className="weather-forcast"> <div className="weather-forcast-days"> <img src={`https://openweathermap.org/img/wn/${forcastWeather.list[5].weather[0].icon}@2x.png`} alt={forcastWeather.list[5].weather[0].description} /> <p className="weather-forcast-days-text">{Math.round(forcastWeather.list[5].main.temp)}°C</p> </div> </div> </div> )} </div> </div> </> ) } export default Weather
内容的提问来源于stack exchange,提问作者Connor Dwyer
相关产品推荐
相关产品推荐

