'Unsubscribe from watchPositionAsync with useEffect return function

Using react native with expo-location for a mobile app, I would like to unsubscribe from Location.watchPositionAsync which returns a promise with a remove() method to unsubscribe. I call the function within a useEffect hooks, but i don't know how to correctly return a cleanup function with the watchPositionAsync promise resolved.

Any suggestions?

import { useState, useEffect } from "react";
import { Text, View } from "react-native";

import * as Location from "expo-location";

export const GpsComponent = function () {
    const [location, setLocation] = useState(null);

    useEffect(() => {
        const positionSubscription = async () => {
            const positionSubscribe = await Location.watchPositionAsync(
                { accuracy: Location.LocationAccuracy.BestForNavigation },
                (newLocation) => {
                    setLocation(newLocation);
                }
            );

            return positionSubscribe;
        };

        /*return () => {
            positionSubscription.remove();
            console.log("Unsubscribed from WatchPositionAsync");
        };*/

    }, [setLocation]);

    return (
        <View>
            <Text>{JSON.stringify(location)}</Text>
        </View>
    );
};


Solution 1:[1]

This will create the watchPositionAsync subscription and pass the correct remove function as the cleanup of the useEffect. A dummy subscription is created initially with a nop remove function.

useEffect(() => {
    // nop subscription. in case not successful
    let subscription = { remove: () => {} }

    // subscribe async function
    const subscribe = async () => {
      return await Location.watchPositionAsync(
        { accuracy: Location.LocationAccuracy.Highest },
        (newLocation) => {
          setLocation(newLocation)
        }
      )
    }

    // return subscription promise
    subscribe()
      .then(result => subscription = result)
      .catch(err => console.warn(err))

    // return remove function for cleanup
    return subscription.remove
  }, [])

Solution 2:[2]

I finally found a way to unsubscribe to watchPositionAsync using useRef

import { useState, useEffect, useRef } from "react";
import { Text, View } from "react-native";

import * as Location from "expo-location";

export const GpsComponent = function () {
    const [location, setLocation] = useState(null);
    const unsubscribe = useRef(() => undefined);

    useEffect(() => {
        const subscribe= async () => {
            
            const positionSubscription = await Location.watchPositionAsync(
                { accuracy: Location.LocationAccuracy.BestForNavigation },
                (newLocation) => {
                    setLocation(newLocation);
                }
            );

           unsubscribe.current=()=>{positionSubscription?.remove()}
        };

     return ()=>{unsubscribe.current()}
    }, []);

    return (
        <View>
            <Text>{JSON.stringify(location)}</Text>
        </View>
    );
};

It 's also possible to use an object and modify a property after the async function's promise is resolved.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Scott Daniel
Solution 2 Fred