React Native自定义useGeolocation Hook调用报Invalid hook call错误求助
React Native自定义Hook调用报错:Invalid hook call问题排查与修复
我在React Native应用中尝试使用自定义Hook useGeolocation 获取用户坐标,但在handleUpdateLocation方法中调用该Hook时,始终出现以下错误:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- You might have more than one copy of React in the same app
我的组件都是函数式组件,以下是组件代码和简化后的Hook代码,想请教问题出在哪里?
组件代码
//import * as React from 'react'; import { View, Text, StyleSheet,TouchableOpacity, Platform,PermissionsAndroid, BackHandler, ScrollView, TextInput, ActivityIndicator ,SafeAreaView} from 'react-native'; import React, { useState, useEffect } from 'react' import { observer } from 'mobx-react' import useGeolocation from '@hooks/useGeolocation.js'; export default OrderCard14 = observer((props) => { const handleUpdateLocation = async (gender) => { const { coordinates, city } = useGeolocation(true); }; return( <View style={{ flex:1,backgroundColor:'white',alignItems:'center',padding:0 }}> </View> ); });
自定义Hook代码
//import AsyncStorage from '@react-native-async-storage/async-storage'; import { AsyncStorage ,Platform, Alert, PermissionsAndroid } from 'react-native'; import _ from 'lodash'; import env from '@env/vars'; import http from '@env/axiosin'; import Geolocation from 'react-native-geolocation-service'; import { useStore } from '@hooks/use-store'; const useGeolocation = (getCityName = false) => { const root = useStore(); const [coordinates, setCoordinates] = useState(false); const [city, setCity] = useState(false); const requestLocationPermissions = async () => { console.log('getting new coordinates'); if (Platform.OS === 'ios') { const auth = await Geolocation.requestAuthorization("whenInUse"); if(auth === "granted") { //root.mapStore.setLocationEnabled(true); this.locationEnabled = true; let coords = await getMe(getCityName); return coords; /* const todayId = moment().isoWeekday(); if(todayId != root.userStore.user.lastLoginDayId) { getMe(); } */ } else { //Alert.alert('PLEASE ENABLE LOCATION'); root.mapStore.setLocationEnabled(false); //this.locationEnabled = false; } } if (Platform.OS === 'android') { let granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION); if (granted === PermissionsAndroid.RESULTS.GRANTED) { // do something if granted... //this.loactionEnabled = true; root.mapStore.setLocationEnabled(true); let coords = await getMe(); return coords; /* const todayId = moment().isoWeekday(); if(todayId != root.userStore.user.lastLoginDayId) { getMe(); } */ } else { //Alert.alert('KULO'); root.mapStore.setLocationEnabled(false); //this.locationEnabled = false; } } } useEffect(() => { requestLocationPermissions(); }, []); console.log('returning new coordinates with hook: '+coordinates); return { coordinates, city }; }; export default useGeolocation;
问题根源
你直接违反了React Hooks的核心规则:Hooks只能在函数组件的顶层作用域调用,绝对不能在嵌套函数(比如handleUpdateLocation)、条件语句或循环中调用。在事件处理函数内部调用useGeolocation,是触发该错误的直接原因。
另外你的useGeolocation还有两处明显问题:
- Hook是函数式逻辑,不存在
this,代码中this.locationEnabled属于错误用法; getMe函数未定义,需要补充实现或修正调用逻辑。
修复方案
第一步:改造自定义Hook
给Hook添加手动触发更新的方法,将位置获取逻辑封装成可调用的函数,同时修正this的错误用法:
//import AsyncStorage from '@react-native-async-storage/async-storage'; import { Platform, Alert, PermissionsAndroid } from 'react-native'; import _ from 'lodash'; import env from '@env/vars'; import http from '@env/axiosin'; import Geolocation from 'react-native-geolocation-service'; import { useStore } from '@hooks/use-store'; import { useState, useEffect } from 'react'; const useGeolocation = (getCityName = false) => { const root = useStore(); const [coordinates, setCoordinates] = useState(false); const [city, setCity] = useState(false); // 封装位置获取核心逻辑,暴露给外部调用 const getLocation = async () => { console.log('getting new coordinates'); if (Platform.OS === 'ios') { const auth = await Geolocation.requestAuthorization("whenInUse"); if (auth === "granted") { root.mapStore.setLocationEnabled(true); // 补充实现getMe函数,传入getCityName参数 const coords = await getMe(getCityName); setCoordinates(coords); // 如果getMe返回城市信息,同步更新city状态 // setCity(...) } else { root.mapStore.setLocationEnabled(false); } } if (Platform.OS === 'android') { let granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION); if (granted === PermissionsAndroid.RESULTS.GRANTED) { root.mapStore.setLocationEnabled(true); const coords = await getMe(getCityName); setCoordinates(coords); } else { root.mapStore.setLocationEnabled(false); } } }; // 组件挂载时自动获取一次位置 useEffect(() => { getLocation(); }, []); console.log('returning new coordinates with hook: ', coordinates); // 返回状态和手动更新方法 return { coordinates, city, getLocation }; }; export default useGeolocation;
第二步:修正组件代码
将Hook移到组件顶层作用域,在事件处理函数中调用Hook返回的getLocation方法:
//import * as React from 'react'; import { View, Text, StyleSheet,TouchableOpacity, Platform,PermissionsAndroid, BackHandler, ScrollView, TextInput, ActivityIndicator ,SafeAreaView} from 'react-native'; import React from 'react' import { observer } from 'mobx-react' import useGeolocation from '@hooks/useGeolocation.js'; export default OrderCard14 = observer((props) => { // 顶层调用Hook,符合Hooks规则 const { coordinates, city, getLocation } = useGeolocation(true); const handleUpdateLocation = async () => { // 调用Hook提供的方法手动更新位置 await getLocation(); // 更新后可直接使用coordinates和city状态 console.log('更新后的坐标:', coordinates); }; return( <View style={{ flex:1,backgroundColor:'white',alignItems:'center',padding:0 }}> {/* 添加触发更新的按钮 */} <TouchableOpacity onPress={handleUpdateLocation} style={{ padding:10, backgroundColor:'#1677ff', marginTop:20 }}> <Text style={{ color:'white' }}>更新位置</Text> </TouchableOpacity> </View> ); });
额外注意事项
- 确保React和React Native版本匹配,避免版本不一致引发的Hook问题;
- 用
npm ls react或yarn list react命令排查项目中是否存在多个React副本; - 补充实现
getMe函数,确保它能正确获取坐标和城市信息并返回。
内容的提问来源于stack exchange,提问作者gabogabans
相关产品推荐
相关产品推荐

