React集成OpenWeather API报错:无法读取undefined的'temp'属性
解决React调用OpenWeather API时的TypeError问题
问题说明
在React项目中调用OpenWeather API获取天气数据,尝试从返回的main对象读取temp、temp_min、temp_max,从weather数组读取main字段时,出现错误:
TypeError: Cannot read properties of undefined (reading 'temp')
错误原因
- 无效请求触发错误响应:
useEffect会在search变化时立即发起请求,包括search为空字符串的情况,此时API返回无main字段的错误响应,导致city.main不存在。 - 未校验API响应状态:输入不存在的城市时,API返回的
cod不为200,响应数据中没有main字段,但代码直接将响应赋值给city,后续渲染访问city.main.temp时报错。 - 条件渲染不严谨:仅通过
!city判断是否渲染天气数据,但city可能是存在但无main属性的错误响应对象。
修复方案
1. 校验API响应,仅保存有效数据
在请求回调中检查响应的cod状态,仅当请求成功时更新city:
const resjson = await res.json(); if (resjson.cod === 200) { setCity(resjson); } else { setCity(null); // 城市不存在时重置状态 }
2. 避免空字符串发起请求
在useEffect中判断search非空时才调用API,减少无效请求:
useEffect(() => { const fetchApi = async () => { try { const url = `https://api.openweathermap.org/data/2.5/weather?q=${search}&units=metric&appid=83ea3057047027c6c4521d32d69250a0`; const res = await fetch(url); const resjson = await res.json(); if (resjson.cod === 200) { setCity(resjson); } else { setCity(null); } } catch (error) { console.log(error); setCity(null); } }; if (search.trim()) { // 非空时才发起请求 fetchApi(); } else { setCity(null); } }, [search]);
3. 严谨的条件渲染与属性访问
渲染时先判断city.main是否存在,同时使用可选链操作符?.避免访问不存在的属性:
{!city ? ( <p>Enter city or country to know weather</p> ) : !city.main ? ( <p>City not found</p> ) : ( // 天气数据渲染代码 <div> <div className="info"> <img src="../images/weather.png" alt="weather icon" className="info-img" /> <h2 className="location"> <BiStreetView className="location-icon" /> {city.name} {/* 使用API返回的城市名更准确 */} </h2> <h1 className="temp">{city.main?.temp} °Cel</h1> <h3 className="tempmin_max"> Min : {city.main?.temp_min} °Cel | Max : {city.main?.temp_max} °Cel </h3> <h3 className="weather-type">Weather: {city.weather?.[0]?.main}</h3> </div> <div className="wave -one"></div> <div className="wave -two"></div> <div className="wave -three"></div> </div> )}
4. 补充未定义的日期时间变量(可选)
原代码中date和time未定义,若需要显示可添加以下逻辑:
const [currentDateTime, setCurrentDateTime] = useState({}); useEffect(() => { const updateDateTime = () => { const now = new Date(); setCurrentDateTime({ date: now.toLocaleDateString(), time: now.toLocaleTimeString() }); }; updateDateTime(); const interval = setInterval(updateDateTime, 1000); return () => clearInterval(interval); }, []);
渲染时使用currentDateTime.date和currentDateTime.time即可。
完整修复后的代码
import React, { useEffect, useState } from "react"; import "./css/style.css"; import { BiStreetView } from "react-icons/bi"; const Tempapp = () => { const [city, setCity] = useState(null); const [search, setSearch] = useState(""); const [currentDateTime, setCurrentDateTime] = useState({}); useEffect(() => { const updateDateTime = () => { const now = new Date(); setCurrentDateTime({ date: now.toLocaleDateString(), time: now.toLocaleTimeString() }); }; updateDateTime(); const interval = setInterval(updateDateTime, 1000); return () => clearInterval(interval); }, []); useEffect(() => { const fetchApi = async () => { try { const url = `https://api.openweathermap.org/data/2.5/weather?q=${search}&units=metric&appid=83ea3057047027c6c4521d32d69250a0`; const res = await fetch(url); const resjson = await res.json(); if (resjson.cod === 200) { setCity(resjson); } else { setCity(null); } } catch (error) { console.log(error); setCity(null); } }; if (search.trim()) { fetchApi(); } else { setCity(null); } }, [search]); return ( <> <div className="box"> <div className="inputData"> <input type="search" className="inputFeild" value={search} placeholder="Enter city" onChange={(event) => { setSearch(event.target.value); }} /> </div> {!city ? ( <p>Enter city or country to know weather</p> ) : !city.main ? ( <p>City not found</p> ) : ( <div> <div className="info"> <img src="../images/weather.png" alt="weather icon" className="info-img" /> <h3 className="dateTime">{currentDateTime.date}</h3> <h3 className="dateTime">{currentDateTime.time}</h3> <h2 className="location"> <BiStreetView className="location-icon" /> {city.name} </h2> <h1 className="temp">{city.main.temp} °Cel</h1> <h3 className="tempmin_max"> Min : {city.main.temp_min} °Cel | Max : {city.main.temp_max} °Cel </h3> <h3 className="weather-type">Weather: {city.weather[0].main}</h3> </div> <div className="wave -one"></div> <div className="wave -two"></div> <div className="wave -three"></div> </div> )} </div> </> ); }; export default Tempapp;
内容的提问来源于stack exchange,提问作者Mustafa Mulla
相关产品推荐
相关产品推荐

