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

React中无法访问WeatherAPI响应对象的二级current子对象问题

问题修复方案

核心问题说明

  • React 组件保留属性冲突:你给 Display 组件传递的 key 是 React 内置的保留属性,仅用于框架内部的列表节点 diff,不会传递到组件的 props 中,导致接口请求无法正常获取密钥,请求失败。需修改为自定义属性名如 apiKey。
  • useEffect 回调规则错误:React 要求 useEffect 的回调函数只能返回清理函数或无返回值,直接传入 async 函数会返回 Promise 对象,就是你收到的 [object Promise] 报错的直接诱因。
  • 状态存储范围不足:你当前仅将接口返回的 location 字段存入 weatherInfo 状态,自然无法访问同层级的 current 字段,需要存储完整的接口返回结果。
  • 嵌套对象渲染错误:current 字段下还包含二级嵌套的 condition 对象,直接遍历对象值渲染时,React 不允许直接把对象作为子元素渲染,会触发 Objects are not valid as a React child 报错。
  • 搜索逻辑缺失:Navbar 中的搜索按钮点击直接触发 setPlaceName,会将事件对象当成地点名称传入,同时表单默认提交行为会导致页面刷新,无法正常查询目标地点天气。

修复后代码

App.js

import React,{useState} from 'react';
import Navbar from './Navbar.js';
import Display from './Display.js'
function App() {
  const[placeName,setPlaceName] = useState('Raipur')
  // 注意替换为你自己的接口密钥
  let apiKey = 'not gonna tell';

  return (
   <>
   <Navbar setPlaceName={setPlaceName} />
   {/* 把key改为自定义属性apiKey,避免和React保留属性冲突 */}
   <Display apiKey={apiKey} placeName={placeName} />
   </>
  );
}

export default App;
import React, { useState } from 'react';

function Navbar(props) {
  // 新增搜索框输入值状态
  const [searchVal, setSearchVal] = useState('');
  // 处理搜索提交
  const handleSearch = (e) => {
    // 阻止表单默认提交刷新页面的行为
    e.preventDefault();
    if(searchVal.trim()){
      props.setPlaceName(searchVal.trim());
    }
  }
  return(
   <>
    <nav className="navbar navbar-expand-lg navbar-dark bg-dark">
  <div className="container-fluid">
    <a className="navbar-brand" href="/">Navbar</a>
    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-target="/navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span className="navbar-toggler-icon"></span>
    </button>
    <div className="collapse navbar-collapse" id="navbarSupportedContent">
      <ul className="navbar-nav me-auto mb-2 mb-lg-0">
        <li className="nav-item">
          <a className="nav-link active" aria-current="page" href="/">Home</a>
        </li>
        <li className="nav-item">
          <a className="nav-link" href="/">Link</a>
        </li>
        <li className="nav-item dropdown">
          <a className="nav-link dropdown-toggle" href="/" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
            Dropdown
          </a>
          <ul className="dropdown-menu" aria-labelledby="navbarDropdown">
            <li><a className="dropdown-item" href="/">Action</a></li>
            <li><a className="dropdown-item" href="/">Another action</a></li>
            <li><hr className="dropdown-divider"/></li>
            <li><a className="dropdown-item" href="/">Something else here</a></li>
          </ul>
        </li>
        <li className="nav-item">
          <a className="nav-link disabled">Disabled</a>
        </li>
      </ul>
      <form className="d-flex" onSubmit={handleSearch}>
        <input 
          className="form-control me-2" 
          type="search" 
          placeholder="搜索地点" 
          aria-label="Search"
          value={searchVal}
          onChange={(e)=>setSearchVal(e.target.value)}
        />
        <button className="btn btn-outline-success" type="submit">搜索</button>
      </form>
    </div>
  </div>
</nav>
   </>
  );
}

export default Navbar;

Display.js

import React,{useState,useEffect} from 'react';

function Display(props) {
  // 初始状态改为对象,对应接口返回的完整结构
  const[weatherInfo,setWeatherInfo] = useState({});
  // 加载状态,避免接口返回前渲染空数据
  const[loading,setLoading] = useState(true);
  
  const getWeatherInfo = async () =>{
    setLoading(true);
    try {
      let url =`https://api.weatherapi.com/v1/current.json?key=${props.apiKey}&q=${props.placeName}&aqi=no`;
      let res = await fetch(url);
      let parsedRes = await res.json();
      // 存储完整的接口返回结果,包含location和current
      setWeatherInfo(parsedRes);
    } catch (err) {
      console.error('天气接口请求失败:', err);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() =>{
    // 不要给useEffect的回调加async,内部调用异步函数即可
    getWeatherInfo();
    // 依赖项加入placeName,切换地点时自动重新查询
  },[props.placeName, props.apiKey])

  // 扁平化处理对象,避免嵌套对象无法直接渲染
  const flattenObj = (obj) => {
    let result = {};
    for(const i in obj){
      if(typeof obj[i] === 'object' && !Array.isArray(obj[i])){
        const temp = flattenObj(obj[i]);
        for(const j in temp){
          result[`${i}.${j}`] = temp[j];
        }
      } else {
        result[i] = obj[i];
      }
    }
    return result;
  }

  if(loading) return <div className="container mt-3">加载中...</div>;
  if(!weatherInfo.location || !weatherInfo.current) return <div className="container mt-3">未查询到对应地点天气信息</div>;

  // 合并location和current的字段用于渲染,你也可以分开渲染两个模块
  const displayData = {...flattenObj(weatherInfo.location), ...flattenObj(weatherInfo.current)};

  return (
    <>
    <div className="container mt-3">
      <h3>{weatherInfo.location.name} 实时天气</h3>
      <div className="row mt-3"> 
      {Object.entries(displayData).map(([key, value])=>{
        return(
         <div className="col-md-3 col-sm-6 mb-2" key={key}>
           <strong>{key}:</strong>{value} 
         </div>
        )  
      })} 
      </div>
    </div>
    </>
  )
}

export default Display;

内容的提问来源于stack exchange,提问作者arsh coding

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 09:30:01