React无按钮调用函数渲染问题:天气应用API调用时机异常
天气应用API调用与组件传参问题
我开发天气应用时遇到两个核心问题:
- 切换按钮控制天气预报展示时,
ThreeDayForecast组件的displayForecast函数不用useEffect就无法触发,用了又会在cityName值准备好前调用API导致报错。 - 给
cityName设置空值默认伦敦的逻辑不生效,但控制台能正确打印cityName。
补充说明:OpenWeather的当前天气接口有准确的高低温、湿度数据,但预报接口数据不准且无湿度,所以采用当前实现方式。
当前代码
ThreeDayForecast组件
const ThreeDayForecast = ( cityName ) => { // 通过Link传递获取原始输入的cityName let api_key = xxx // 不用useEffect的话displayForecast不会被调用 const displayForecast = async(cityName) => { console.log({ cityName }) console.log("testing") if(cityName ==="") { cityName = "London" } let url = `https://api.openweathermap.org/data/2.5/forecast?q=${cityName}&units=Imperial&appid=${api_key}` let response = await fetch(url) let data = await response.json() // 其他处理代码 } useEffect(() => { displayForecast(); }) return ( // 渲染代码 ) }
ForecastButton组件
function toggle() { setIsOpen((isOpen) => !isOpen) } return ( <div className="container"> {label}{" "} <div className="toggle-switch"> <input type="checkbox" className="checkbox" name={label} id={label} onClick={toggle} /> <label className="label" htmlFor={label}> <span className="inner" /> <span className="switch" /> </label> <div style={{ display: isOpen ? 'block' : 'none' }}> <ThreeDayForecast /> </div>
问题分析
- 组件传参格式错误:React组件的props是一个对象,
ThreeDayForecast直接写(cityName)会把整个props对象赋值给cityName,而非传入的城市名字符串。 - useEffect无依赖且未传参:当前
useEffect没有依赖数组,会在组件每次渲染时触发;且调用displayForecast()时未传参,导致函数内部cityName为undefined,空值判断逻辑失效。 - 父组件未传递cityName:
ForecastButton渲染ThreeDayForecast时没有传入cityName属性,子组件根本无法获取目标城市值。
修复方案
1. 修正ThreeDayForecast组件逻辑
import { useEffect } from 'react'; const ThreeDayForecast = ({ cityName }) => { // 解构props获取正确的cityName const api_key = 'xxx'; // 建议用环境变量存储密钥,避免硬编码 const displayForecast = async(targetCity) => { console.log({ targetCity }); // 处理空值或undefined,默认设为伦敦 const finalCity = targetCity?.trim() || "London"; const url = `https://api.openweathermap.org/data/2.5/forecast?q=${finalCity}&units=Imperial&appid=${api_key}`; try { const response = await fetch(url); if (!response.ok) throw new Error('API请求失败'); const data = await response.json(); // 数据处理逻辑 } catch (err) { console.error(err); // 错误提示逻辑(比如显示错误文案) } }; // 依赖数组添加cityName,仅当城市名变化时触发API调用 useEffect(() => { displayForecast(cityName); }, [cityName]); return ( // 渲染代码 ); };
2. 父组件传递cityName
在ForecastButton中渲染子组件时传入cityName(假设父组件已持有该值):
// 假设ForecastButton组件可获取到cityName变量 return ( <div className="container"> {label}{" "} <div className="toggle-switch"> <input type="checkbox" className="checkbox" name={label} id={label} onClick={toggle} /> <label className="label" htmlFor={label}> <span className="inner" /> <span className="switch" /> </label> <div style={{ display: isOpen ? 'block' : 'none' }}> {/* 传入cityName给子组件 */} <ThreeDayForecast cityName={cityName} /> </div> </div> </div> );
额外优化建议
- API密钥存入环境变量(比如React项目的
.env文件中设置REACT_APP_OPENWEATHER_KEY),调用时用process.env.REACT_APP_OPENWEATHER_KEY,避免代码泄露密钥。 - 添加加载状态,在API请求过程中显示加载动画,提升用户体验。
内容的提问来源于stack exchange,提问作者cwa
相关产品推荐
相关产品推荐

