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

如何在useEffect中调用函数解决Invalid Hook Call错误

错误原因

抛出Invalid Hook Call是因为违反了React Hooks的核心调用规则:useDispatch 这类Hook只能在React函数组件顶层、自定义Hook内部调用。你把useDispatch写在了普通异步工具函数getLocation中,这个函数既不是React组件也不是自定义Hook,自然会触发报错。另外原代码里的setErrorMsg没有对应声明,本身也是未定义变量,运行时也会抛出异常。

可直接落地的修复方案

二选一即可,推荐优先用方案2,逻辑内聚性更好。

方案1:将dispatch作为参数传入工具函数

改造原有getLocation,不在内部调用任何Hook:

export async function getLocation(dispatch, onError) {
  let { status } = await Location.requestForegroundPermissionsAsync();

  if (status !== "granted") {
    onError?.("Permission to access location was denied");
    return;
  }

  let location = await Location.getCurrentPositionAsync({
    enableHighAccuracy: true,
    accuracy: Location.Accuracy.BestForNavigation,
    maximumAge: 1000,
  });

  dispatch(
    setLocation({
      longitude: location?.coords?.longitude,
      latitude: location?.coords?.latitude,
    })
  );
}

在组件顶层调用useDispatch拿到实例后传入函数:

import { useDispatch } from 'react-redux';
import { useState, useEffect } from 'react';

const Navigation = () => {
  const dispatch = useDispatch();
  const [errorMsg, setErrorMsg] = useState(null);

  useEffect(() => {
    getLocation(dispatch, setErrorMsg);
  }, [dispatch]);

  // 权限拒绝时可在这里做提示,比如Toast、弹窗
  if (errorMsg) console.warn(errorMsg);

  return (
    <NavigationContainer>
      <RootStackScreen />
    </NavigationContainer>
  );
};

方案2:封装为自定义Hook

自定义Hook以use开头命名,符合Hook规则即可内部调用其他Hook,逻辑更易复用:

import { useDispatch } from 'react-redux';
import { useState, useCallback } from 'react';

export function useGetLocation() {
  const dispatch = useDispatch();
  const [errorMsg, setErrorMsg] = useState(null);
  const [loading, setLoading] = useState(false);

  const fetchLocation = useCallback(async () => {
    setLoading(true);
    try {
      let { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== "granted") {
        setErrorMsg("Permission to access location was denied");
        return;
      }

      let location = await Location.getCurrentPositionAsync({
        enableHighAccuracy: true,
        accuracy: Location.Accuracy.BestForNavigation,
        maximumAge: 1000,
      });

      dispatch(
        setLocation({
          longitude: location?.coords?.longitude,
          latitude: location?.coords?.latitude,
        })
      );
    } catch (err) {
      setErrorMsg(err.message);
    } finally {
      setLoading(false);
    }
  }, [dispatch]);

  return { fetchLocation, errorMsg, loading };
}

组件中直接调用自定义Hook即可:

const Navigation = () => {
  const { fetchLocation, errorMsg, loading } = useGetLocation();

  useEffect(() => {
    fetchLocation();
  }, [fetchLocation]);

  return (
    <NavigationContainer>
      <RootStackScreen />
    </NavigationContainer>
  );
};
位置获取优化建议
  • 补全异常捕获:原代码无catch逻辑,GPS信号弱、系统定位服务关闭等场景会抛出未捕获的Promise错误,必须用try/catch包裹异步流程
  • 增加加载状态:定位是异步过程,添加loading态给用户明确反馈,避免用户以为应用无响应
  • 权限二次引导:用户拒绝权限后,可引导用户跳转系统设置页手动开启权限,不要直接终止流程
  • 精度降级策略:高精度定位超时(比如超过5s未返回)时,自动降级为低精度定位,减少用户等待时间
  • 位置缓存:设置合理的缓存有效期,短时间内重复进入应用不需要重复调用定位接口,降低性能消耗
  • 权限按需申请:如果应用不是必须拿到位置才能使用,不要在启动时直接弹权限申请,可以在用户用到位置相关功能时再触发申请,降低用户拒绝概率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 10:46:00