移动端天气API数据加载异常:Loader持续显示不渲染内容排查
移动端天气应用Loader一直显示不渲染数据的问题排查与解决
我通过API获取天气数据,用Loader组件做条件渲染。页面有时能正常展示数据,但手机端经常出现Loader一直转、从不渲染数据的情况,电脑端没问题。相关代码如下:
useEffect代码
useEffect(() => { navigator.geolocation && navigator.geolocation.getCurrentPosition((position) => { axios .get( `https://api.openweathermap.org/data/2.5/weather?lat=${position.coords.latitude}&lon=${position.coords.longitude}&appid=${process.env.REACT_APP_WEATHER_API_KEY}&units=${metric_units}` ) .then((response) => { console.log(response.data); setData(response.data); }) .catch((error) => { alert('Network Error'); console.error('API call error:', error); }); }); }, []);
渲染相关代码
let localDay = new Date((data.dt + data.timezone) * 1000).getUTCDay(); let localDate = new Date((data.dt + data.timezone) * 1000).getUTCDate(); let localMonth = new Date((data.dt + data.timezone) * 1000).getUTCMonth(); let localHours = new Date((data.dt + data.timezone) * 1000).getUTCHours(); let localMinutes = new Date((data.dt + data.timezone) * 1000).getUTCMinutes(); {Object.keys(data).length === 0 ? ( <PuffLoader color={'#ffffff'} size={200} className='loader' /> ) : ( <div className='content'> {/* ********** TOP-SECTION ********** */} <section className='top-section'> <div className='location'> <p>{data.name}</p> {data.sys && <p>, {data.sys.country}</p>} </div> <div className='date-time'> <p className='date'> {weekdays[localDay]}, {months[localMonth]} {localDate} </p> <p className='time'> {localHours < 10 ? '0' + localHours : '' + localHours}: {localMinutes < 10 ? '0' + localMinutes : '' + localMinutes} </p> </div> <div className='temp-icon'> <div className='temp'> {data.main && <h1>{data.main.temp.toFixed()} °C</h1>} </div> <div className='icon-div'> {data.weather && ( <img className='icon' src={`http://openweathermap.org/img/wn/${data.weather[0].icon}@2x.png`} alt='weather icon' ></img> )} </div> <div className='description'> {data.weather && <p>{data.weather[0].description}</p>} </div> </div> <div className='description-feels-like'> <div className='feels-like'> {data.main && ( <p>Feels like {data.main.feels_like.toFixed()} °C</p> )} </div> </div> </section> {/* ********** BOTTOM-SECTION ********** */} {data.name !== undefined && ( <section className='bottom-section'> <div className='humidity'> {data.main && <p className='bold'>{data.main.humidity} %</p>} <p>Humidity</p> </div> <div className='pressure'> {data.main && <p className='bold'>{data.main.pressure} mb</p>} <p>Pressure</p> </div> <div className='wind'> {data.wind && ( <p className='bold'>{data.wind.speed.toFixed()} m/s</p> )} <p>Wind</p> </div> </section> )} </div> )}
问题根源
- 地理位置获取失败无处理:
getCurrentPosition只定义了成功回调,未处理权限拒绝、位置获取失败等异常。手机端用户常关闭位置权限或设备无法定位,导致API从未调用,data始终为空,Loader持续显示。 - 空数据时日期计算报错:
localDay等变量在渲染阶段直接计算,当data为空时,data.dt和data.timezone均为undefined,计算后得到NaN,传入new Date()生成无效日期,部分移动端浏览器会因该错误卡住渲染,无法更新状态。 - 加载状态判断不可靠:用
Object.keys(data).length === 0判断加载状态,若data被意外设为null或非对象值,判断逻辑会失效。
修复方案
1. 完善地理位置获取的错误处理
给getCurrentPosition添加错误回调,处理各类位置获取失败场景,提示用户并终止加载:
useEffect(() => { if (!navigator.geolocation) { alert('浏览器不支持地理位置功能'); return; } navigator.geolocation.getCurrentPosition( (position) => { axios .get( `https://api.openweathermap.org/data/2.5/weather?lat=${position.coords.latitude}&lon=${position.coords.longitude}&appid=${process.env.REACT_APP_WEATHER_API_KEY}&units=${metric_units}` ) .then((response) => { setData(response.data); }) .catch((error) => { alert('获取天气数据失败,请检查网络'); console.error('API call error:', error); }); }, (error) => { let errorMsg = ''; switch(error.code) { case error.PERMISSION_DENIED: errorMsg = '请开启位置权限以获取天气数据'; break; case error.POSITION_UNAVAILABLE: errorMsg = '无法获取当前位置'; break; case error.TIMEOUT: errorMsg = '获取位置超时'; break; default: errorMsg = '获取位置失败'; } alert(errorMsg); console.error('Geolocation error:', error); } ); }, []);
2. 延迟日期计算到数据存在时
将日期计算逻辑放到data存在的分支内,避免空值报错:
{Object.keys(data).length === 0 ? ( <PuffLoader color={'#ffffff'} size={200} className='loader' /> ) : ( <> {(() => { const timestamp = (data.dt + data.timezone) * 1000; const localDay = new Date(timestamp).getUTCDay(); const localDate = new Date(timestamp).getUTCDate(); const localMonth = new Date(timestamp).getUTCMonth(); const localHours = new Date(timestamp).getUTCHours(); const localMinutes = new Date(timestamp).getUTCMinutes(); return ( <div className='content'> {/* 原content内容,使用上述变量 */} </div> ); })()} </> )}
3. 使用独立加载状态变量
用isLoading状态控制Loader显示,比判断data更可靠:
const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { if (!navigator.geolocation) { alert('浏览器不支持地理位置功能'); setIsLoading(false); return; } navigator.geolocation.getCurrentPosition( (position) => { axios .get(/* API地址 */) .then((response) => { setData(response.data); setIsLoading(false); }) .catch((error) => { alert('获取天气数据失败'); console.error(error); setIsLoading(false); }); }, (error) => { alert('获取位置失败'); setIsLoading(false); } ); }, []); // 渲染逻辑 {isLoading ? ( <PuffLoader color={'#ffffff'} size={200} className='loader' /> ) : data ? ( <div className='content'>{/* 内容 */}</div> ) : ( <p>加载失败,请重试</p> )}
内容的提问来源于stack exchange,提问作者Igor Gryp
相关产品推荐
相关产品推荐

