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

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')

错误原因

  1. 无效请求触发错误响应:useEffect会在search变化时立即发起请求,包括search为空字符串的情况,此时API返回无main字段的错误响应,导致city.main不存在。
  2. 未校验API响应状态:输入不存在的城市时,API返回的cod不为200,响应数据中没有main字段,但代码直接将响应赋值给city,后续渲染访问city.main.temp时报错。
  3. 条件渲染不严谨:仅通过!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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 16:10:21