React中useEffect调用数据问题:农场天气API参数undefined报错
问题:调用OpenWeatherMap API时出现400错误(lat/lon为undefined)
我尝试通过农场的经纬度获取天气数据,已经成功从API拿到农场数据并传入React组件,但调用OpenWeatherMap接口时一直报错:GET http://api.openweathermap.org/data/2.5/weather?lat=undefined&lon=undefined&appid=[API]&units=metric 400 (Bad Request)
相关代码
FarmDetails组件
import React, { useState, useEffect } from "react"; import { Link, useNavigate, useParams } from "react-router-dom"; import Menubar from "../../components/menubar/Menubar"; import Skeleton from "react-loading-skeleton"; import toast, { Toaster } from 'react-hot-toast'; import axios from 'axios'; import { LazyLoadImage } from "react-lazy-load-image-component"; import Weather from "../../components/weather/Weather"; import FarmWeather from "../../components/weather/FarmWeather"; function FarmDetails() { let navigate = useNavigate(); const [farm, setFarm] = useState(''); const { username } = useParams(); const { farmId } = useParams(); const [isLoading, setIsLoading] = useState(true); useEffect(() => { let isMounted = true; axios.get(`/api/farm/${username}/${farmId}`).then(res => { if (isMounted) { if (res.data.status === 200) { setFarm(res.data.farm); setIsLoading(false); console.warn(res.data.farm) } else if (res.data.status === 404) { navigate('/'); toast.error(res.data.message, "error"); } } }); return () => { isMounted = false }; }, []); return ( <div> <Menubar /> <div className="appHeader bg-primary text-light"> <div className="left"> <a onClick={() => navigate(-1)} className="headerButton goBack"> <i className="fi fi-rr-angle-left"></i> </a> </div> <div className="pageTitle">{farm.farmname}</div> <div className="right"></div> </div> <div id="appCapsule"> <div className="section mt-3 mb-3"> <div className="card"> <div className="card-header"> Discover </div> <div className="card-body"> <h5 className="card-title">Farm Supplies</h5> <p className="card-text">Order for farm supplies suitable for your farm and grow healthy fruits</p> <a href="shop.php" className="btn btn-primary spinner-btn">Go to Shop</a> </div> </div> </div> <FarmWeather farm={farm} /> <div className="section mt-3 mb-3"> <div className="row"> <div className="col-6"> <div className="card card-normal"> <div className="card-body"> <h5 className="card-title title-small mb-2">Location</h5> <div className="card-icon mb-0"> <i className="fi fi-rr-marker"></i> </div> </div> </div> </div> <div className="col-6"> <div className="card card-normal card-add"> <a href="farm-fruits.php?view=<?php echo $row['farmid']; ?>"> <div className="card-body"> <h5 className="card-title title-small mb-2">Fruits</h5> <div className="card-icon mb-0"> <i className="fi fi-rr-apple-whole"></i> </div> </div> </a> </div> </div> <div className="col-6"> <div className="card card-normal"> <div className="card-body"> <h5 className="card-title title-small mb-2">Sales</h5> <div className="card-icon mb-0"> <i className="fi fi-rr-bolt"></i> </div> </div> </div> </div> <div className="col-6"> <div className="card card-normal"> <div className="card-body"> <h5 className="card-title title-small mb-2">Expenses</h5> <div className="card-icon mb-0"> <i className="fi fi-rr-settings"></i> </div> </div> </div> </div> </div> </div> </div> </div> ); } export default FarmDetails;
FarmWeather组件
import React, { useState, useEffect } from "react"; const FarmWeather = ({ farm }) => { const [weather, setWeather] = useState({}); useEffect(() => { fetch(`//api.openweathermap.org/data/2.5/weather?lat=${farm.latitude}&lon=${farm.longitude}&appid=[API]&units=metric`) .then((result) => result.json()) .then((weather) => { setWeather(weather); }); }, []); console.warn("result", weather) return ( <div className="section mt-3 mb-3"> <div className="card text-white bg-primary mb-2"> <div className="card-header">Weather in <span>{weather.name}</span> <div className="weather-icon"> <img src={`http://openweathermap.org/img/w/${weather.weather && weather.weather[0].icon}.png`} alt="weather icon" /> </div> </div> <div className="card-body"> <h5 className="card-title">Temp <span>{weather.main && weather.main.temp} °C</span></h5> <p className="card-text">Our Technologies have discovered that the Current Weather around your Farm in {farm.county && farm.county.countyname} is Mostly <span id="description" ></span> </p> </div> </div> </div> ); } export default FarmWeather;
问题原因
- 初始状态错误:
FarmDetails中farm的初始值是空字符串'',组件挂载时FarmWeather接收的farm是字符串,访问farm.latitude和farm.longitude会得到undefined。 - Effect依赖缺失:
FarmWeather的useEffect依赖数组为空,只会执行一次,此时农场数据还未从API加载完成,导致拼接URL时使用了undefined的经纬度。
解决方案
1. 修正farm初始状态
在FarmDetails组件中,将farm的初始状态改为空对象,避免访问字符串属性出错:
const [farm, setFarm] = useState({});
2. 给Effect添加正确依赖并增加校验
修改FarmWeather的useEffect,添加经纬度作为依赖,并在调用API前校验经纬度是否存在,避免无效请求:
useEffect(() => { // 确保经纬度存在再发起请求 if (farm.latitude && farm.longitude) { fetch(`//api.openweathermap.org/data/2.5/weather?lat=${farm.latitude}&lon=${farm.longitude}&appid=[API]&units=metric`) .then((result) => result.json()) .then((weather) => { setWeather(weather); }); } }, [farm.latitude, farm.longitude]);
3. 可选:添加条件渲染优化体验
利用FarmDetails中的isLoading状态,在农场数据加载完成前不渲染FarmWeather:
// 在FarmDetails的return中替换原<FarmWeather/>代码 {!isLoading && <FarmWeather farm={farm} />}
或者在FarmWeather中添加加载状态提示:
const FarmWeather = ({ farm }) => { const [weather, setWeather] = useState({}); useEffect(() => { if (farm.latitude && farm.longitude) { fetch(`//api.openweathermap.org/data/2.5/weather?lat=${farm.latitude}&lon=${farm.longitude}&appid=[API]&units=metric`) .then((result) => result.json()) .then((weather) => { setWeather(weather); }); } }, [farm.latitude, farm.longitude]); // 经纬度未加载时显示提示 if (!farm.latitude || !farm.longitude) { return <div className="section mt-3 mb-3"><div className="card text-white bg-primary mb-2"><div className="card-body">加载天气数据中...</div></div></div>; } return ( // 原渲染代码 ); }
内容的提问来源于stack exchange,提问作者John Mark
相关产品推荐
相关产品推荐

