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

移动端天气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>
)}

问题根源

  1. 地理位置获取失败无处理:getCurrentPosition只定义了成功回调,未处理权限拒绝、位置获取失败等异常。手机端用户常关闭位置权限或设备无法定位,导致API从未调用,data始终为空,Loader持续显示。
  2. 空数据时日期计算报错:localDay等变量在渲染阶段直接计算,当data为空时,data.dt和data.timezone均为undefined,计算后得到NaN,传入new Date()生成无效日期,部分移动端浏览器会因该错误卡住渲染,无法更新状态。
  3. 加载状态判断不可靠:用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 18:57:03