React Native: Error: Invalid hook call. Hooks can only be called inside of the body of a function component

Viewed 67

in my react native app I have started playing with custom hooks, I created a hook to retrive user coordinates, however when my useGeolocation hook is called (inside handleUpdateLocation method) I always get the following warning:

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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

Actually, all my components are functional components...

Here is my component:

//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>
    
    
    );
        

});

And my simplified hook(removed some functions):

//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;

What's exactly the problem?

1 Answers

Invalid hook call. Hooks can only be called inside of the body of a function component.

I think they should add to the list of possible/common reasons that:

  • You might be conditionally invoking the hook call on any given render, or not invoking it at all.

Hooks not only need to be inside the body of a function component, they need to be at the level of the body of the function component. Always called on every render, always in the same order. You have one that's inside a function:

const handleUpdateLocation = async (gender) =>
{
    const { coordinates, city } = useGeolocation(true);
};

Which is invalid. Instead, move the hook call to the component level. You can still use the resulting values inside the function:

const { coordinates, city } = useGeolocation(true);

const handleUpdateLocation = async (gender) =>
{
    // here you can still use coordinates and city
};

In general, it's a good rule of thumb to invoke all of the hooks that you'll need as the very first thing you do in any given component.

Related