【问题标题】:Geolocation clearWatch(watchId) does not stop location tracking (React Native)Geolocation clearWatch(watchId) 不会停止位置跟踪(React Native)
【发布时间】:2021-01-18 22:27:26
【问题描述】:

我正在尝试创建位置跟踪器的简单示例,但我遇到了以下情况。我的基本目标是通过按开始/结束按钮来切换位置手表。我通过实现自定义反应钩子来分离关注点,然后在 App 组件中使用它:

useWatchLocation.js

import {useEffect, useRef, useState} from "react"
import {PermissionsAndroid} from "react-native"
import Geolocation from "react-native-geolocation-service"

const watchCurrentLocation = async (successCallback, errorCallback) => {
  if (!(await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION))) {
    errorCallback("Permissions for location are not granted!")
  }
  return Geolocation.watchPosition(successCallback, errorCallback, {
    timeout: 3000,
    maximumAge: 500,
    enableHighAccuracy: true,
    distanceFilter: 0,
    useSignificantChanges: false,
  })
}

const stopWatchingLocation = (watchId) => {
  Geolocation.clearWatch(watchId)
  // Geolocation.stopObserving()
}

export default useWatchLocation = () => {
  const [location, setLocation] = useState()
  const [lastError, setLastError] = useState()
  const [locationToggle, setLocationToggle] = useState(false)
  const watchId = useRef(null)

  const startLocationWatch = () => {
    watchId.current = watchCurrentLocation(
      (position) => {
        setLocation(position)
      },
      (error) => {
        setLastError(error)
      }
    )
  }

  const cancelLocationWatch = () => {
    stopWatchingLocation(watchId.current)
    setLocation(null)
    setLastError(null)
  }

  const setLocationWatch = (flag) => {
    setLocationToggle(flag)
  }

  // execution after render when locationToggle is changed
  useEffect(() => {
    if (locationToggle) {
      startLocationWatch()
    } else cancelLocationWatch()
    return cancelLocationWatch()
  }, [locationToggle])

  // mount / unmount
  useEffect(() => {
    cancelLocationWatch()
  }, [])

  return { location, lastError, setLocationWatch }
}

App.js

import React from "react"
import {Button, Text, View} from "react-native"

import useWatchLocation from "./hooks/useWatchLocation"

export default App = () => {
  const { location, lastError, setLocationWatch } = useWatchLocation()
  return (
    <View style={{ margin: 20 }}>
      <View style={{ margin: 20, alignItems: "center" }}>
        <Text>{location && `Time: ${new Date(location.timestamp).toLocaleTimeString()}`}</Text>
        <Text>{location && `Latitude: ${location.coords.latitude}`}</Text>
        <Text>{location && `Longitude: ${location.coords.longitude}`}</Text>
        <Text>{lastError && `Error: ${lastError}`}</Text>
      </View>
      <View style={{ marginTop: 20, width: "100%", flexDirection: "row", justifyContent: "space-evenly" }}>
        <Button onPress={() => {setLocationWatch(true)}} title="START" />
        <Button onPress={() => {setLocationWatch(false)}} title="STOP" />
      </View>
    </View>
  )
}

我已经搜索了多个在线示例,上面的代码应该可以工作。但问题是当按下停止按钮时,即使我调用 Geolocation.clearWatch(watchId),位置仍然会不断更新。

我封装了 Geolocation 调用来处理位置许可和其他可能的调试内容。似乎使用 useWatchLocation 内的 useRef 挂钩保存的 watchId 值无效。我的猜测是基于尝试在 Geolocation.clearWatch(watchId) 之后立即调用 Geolocation.stopObserving()。订阅停止但我收到警告:

使用现有订阅调用 stopObserving。

所以我假设原始订阅没有被清除。

我错过了什么/做错了什么?

编辑:我想出了解决方案。但是由于 isMounted 模式通常被认为是反模式:有人有更好的解决方案吗?

【问题讨论】:

    标签: react-native geolocation react-hooks react-functional-component use-ref


    【解决方案1】:

    好的,使用 isMounted 模式 解决了问题。 isMounted.current 设置为locationToggle 效果到true 和内部cancelLocationWatchfalse

    const isMounted = useRef(null)
    
    ...
        
    useEffect(() => {
            if (locationToggle) {
              isMounted.current = true              // <--
              startLocationWatch()
            } else cancelLocationWatch()
            return () => cancelLocationWatch()
          }, [locationToggle])
        
    ...    
    
    const cancelLocationWatch = () => {
            stopWatchingLocation(watchId.current)
            setLocation(null)
            setLastError(null)
            isMounted.current = false               // <--
          }
    

    并检查挂载/卸载效果、成功和错误回调:

    const startLocationWatch = () => {
        watchId.current = watchCurrentLocation(
          (position) => {
            if (isMounted.current) {                // <--
              setLocation(position)
            }
          },
          (error) => {
            if (isMounted.current) {                // <--
              setLastError(error)
            }
          }
        )
      }
    

    【讨论】:

    • 我认为这是 isMounted 模式的合理用例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-04
    • 1970-01-01
    相关资源
    最近更新 更多