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

React Native天气应用首次渲染时useEffect异步函数失效问题

问题

开发React Native天气应用时,首次渲染通过useEffect触发异步流程:请求位置权限→获取定位→调用天气API。首次获取定位成功,但调用天气API时抛出TypeError: Cannot read property 'coords' of null,二次渲染后API调用正常。

相关代码

// States
const [loading, setLoading] = useState(true);
const [location, setLocation] = useState(null);
const [error, setError] = useState(null);

// useEffect
useEffect(() => {
  requestLocationPermission();
}, []);

// 请求位置权限
const requestLocationPermission = async () => {
    console.log('requestLocationPermission started');
    try {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
        );
        if (granted === PermissionsAndroid.RESULTS.GRANTED) {
          console.log('You can use the location');
          getLocation();
        } else {
          console.log('Location permission denied');
        }
      } catch (err) {
        console.warn(err);
      }
      console.log('requestLocationPermission finished');
    };

// 获取定位信息
const getLocation = async () => {
    console.log('getLocation started');
    try {
      await Geolocation.getCurrentPosition(position => {
        setLocation(position);
        console.log(position);
        getWeather();
      });
    } catch (err) {
      console.log('Err:' + err);
    }
    console.log('getLocation finished');
  };

// 调用天气API
const getWeather = async () => {
    console.log('getWeather started');
    try {
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?lat=${location['coords']['latitude']}&lon=${location['coords']['longitude']}&appid=${WEATHER_API_KEY}`,
        ).then(response => response.json());
        console.log('response');
        console.log(response);
      } catch (err) {
        console.log('Err:' + err);
      }
      console.log('getWeather finished');
  };

首次渲染控制台日志

info Reloading app...
 BUNDLE  ./index.js

 LOG  Running "WeatherApp" with {"rootTag":331}
 LOG  requestLocationPermission started
 LOG  You can use the location
 LOG  getLocation started
 LOG  requestLocationPermission finished
 LOG  getLocation finished
 LOG  {"coords": {"accuracy": 600, "altitude": 0, "heading": 0, "latitude": 37.4220936, "longitude": -122.083922, "speed": 0}, "extras": {"networkLocationType": "cell"}, "mocked": false, "timestamp": 1682430365994}
 LOG  getWeather started
 LOG  Err:TypeError: Cannot read property 'coords' of null
 LOG  getWeather finished

二次渲染控制台日志

info Reloading app...
 BUNDLE  ./index.js

 LOG  Running "WeatherApp" with {"rootTag":331}
 LOG  requestLocationPermission started
 LOG  You can use the location
 LOG  getLocation started
 LOG  requestLocationPermission finished
 LOG  getLocation finished
 LOG  {"coords": {"accuracy": 600, "altitude": 0, "heading": 0, "latitude": 37.4220936, "longitude": -122.083922, "speed": 0}, "extras": {"networkLocationType": "cell"}, "mocked": false, "timestamp": 1682430365994}
 LOG  getWeather started
 LOG  Err:TypeError: Cannot read property 'coords' of null
 LOG  getWeather finished
 LOG  requestLocationPermission started
 LOG  You can use the location
 LOG  getLocation started
 LOG  requestLocationPermission finished
 LOG  getLocation finished
 LOG  {"coords": {"accuracy": 600, "altitude": 0, "heading": 0, "latitude": 37.4220936, "longitude": -122.083922, "speed": 0}, "extras": {"networkLocationType": "cell"}, "mocked": false, "timestamp": 1682432167007}
 LOG  getWeather started
 LOG  response
 LOG  {"base": "stations", "clouds": {"all": 0}, "cod": 200, "coord": {"lat": 37.4221, "lon": -122.0839}, "dt": 1682431628, "id": 5375480, "main": {"feels_like": 281.24, "humidity": 85, "pressure": 1013, "temp": 281.82, "temp_max": 284, "temp_min": 280.34}, "name": "Mountain View", "sys": {"country": "US", "id": 2010364, "sunrise": 1682428825, "sunset": 1682477513, "type": 2}, "timezone": -25200, "visibility": 10000, "weather": [{"description": "mist", "icon": "50d", "id": 701, "main": "Mist"}], "wind": {"deg": 180, "speed": 1.54}}
 LOG  getWeather finished

原因分析

核心问题是React的setState是异步操作:在getCurrentPosition的回调里调用setLocation(position)后,组件并不会立刻更新location状态,此时getWeather函数访问的location还是初始值null,因此触发Cannot read property 'coords' of null错误。

二次渲染时,location已经被更新为有效的定位数据,所以再次调用getWeather时能正常访问coords属性。

解决办法

提供三种可行方案,按需选择:

方案1:直接传递定位数据给getWeather

不需要依赖location状态,直接把getCurrentPosition返回的position作为参数传给getWeather,避免等待状态更新:

const getLocation = async () => {
    console.log('getLocation started');
    try {
      await Geolocation.getCurrentPosition(position => {
        setLocation(position);
        console.log(position);
        // 直接传入position
        getWeather(position);
      });
    } catch (err) {
      console.log('Err:' + err);
    }
    console.log('getLocation finished');
  };

const getWeather = async (position) => {
    console.log('getWeather started');
    try {
      // 使用传入的position,而非state中的location
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?lat=${position['coords']['latitude']}&lon=${position['coords']['longitude']}&appid=${WEATHER_API_KEY}`,
        ).then(response => response.json());
        console.log('response');
        console.log(response);
      } catch (err) {
        console.log('Err:' + err);
      }
      console.log('getWeather finished');
  };

方案2:通过useEffect监听location变化

新增一个useEffect,当location状态变为有效数据时自动调用getWeather:

// 新增useEffect,监听location变化
useEffect(() => {
  if (location) {
    getWeather();
  }
}, [location]);

// 修改getLocation,移除内部的getWeather调用
const getLocation = async () => {
    console.log('getLocation started');
    try {
      await Geolocation.getCurrentPosition(position => {
        setLocation(position);
        console.log(position);
        // 这里不再调用getWeather
      });
    } catch (err) {
      console.log('Err:' + err);
    }
    console.log('getLocation finished');
  };

方案3:在getWeather中增加空值判断

在访问location.coords前先检查location是否存在,避免报错:

const getWeather = async () => {
    console.log('getWeather started');
    // 先判断location是否有效
    if (!location) {
      console.log('Location not available yet');
      return;
    }
    try {
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?lat=${location['coords']['latitude']}&lon=${location['coords']['longitude']}&appid=${WEATHER_API_KEY}`,
        ).then(response => response.json());
        console.log('response');
        console.log(response);
      } catch (err) {
        console.log('Err:' + err);
      }
      console.log('getWeather finished');
  };

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 04:57:01