在本地反应中使用带有地理定位的钩子/状态我哪里出错了?

Posted

技术标签:

【中文标题】在本地反应中使用带有地理定位的钩子/状态我哪里出错了?【英文标题】:Where am I going wrong using hooks/state with geolocation in react native? 【发布时间】:2020-07-16 17:39:01 【问题描述】:

我有一个组件应该定期记录经度/纬度/时间戳

当用户按下 START 时,应该开始跟踪。当用户按下 STOP 时,跟踪应该停止。

为了实现这一点,我构建了以下内容(我是一个完整的 react 和 JS 初学者,所以这可能是完全错误的方法):

const Tracking = props => 

  const [currentLatitude, setCurrentLatitude] = useState(0);
  const [currentLongitude, setCurrentLongitude] = useState(0);
  const [currentTimestamp, setCurrentTimestamp] = useState(0);

  const [buttonTitle, setButtonTitle] = useState('Start');
  const [isTracking, setIsTracking] = useState(false);
  var getLocationInterval;


  function getLocation() 
    navigator.geolocation.getCurrentPosition(
      position => 
        setCurrentLongitude(position.coords.longitude);
        setCurrentLatitude(position.coords.latitude);
        setCurrentTimestamp(position.timestamp);
      ,
      error => alert(error.message),
       enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 
    );
    console.log(currentTimestamp, currentLatitude, currentLongitude);
  ;

  function startStop() 
    if(!isTracking)
      //START
      setIsTracking(true);
      getLocationInterval = setInterval(getLocation, 500);
      setButtonTitle('Stop');
    
    else
      //STOP
      setIsTracking(false);
      clearInterval(getLocationInterval);
      setButtonTitle('Start');
    
  ;


  return (
    <View style=width: '100%', height: '100%'>
      <MapView showsUserLocation style=flex: 1 />
      <MenuButton title = buttonTitle onPress=startStop/>
    </View>
  );


预期行为:一旦按下 START,按钮文本将变为 STOP。在我的控制台中,我开始每 500 毫秒使用最新的 lat/long/timestamp 获得一个输出。当按下 STOP 时,按钮文本变为 START 并且输出停止。

实际行为:一旦按下 START,按钮文本正确更改为 STOP,但只会重复输出初始状态 (0s)。然后当我按下 STOP 时,下一个纬度/经度/时间戳开始重复输出到控制台。 0 仍在输出,因为间隔似乎没有停止。

我猜我只是在这里使用完全错误的状态。请问有人可以帮我吗?

【问题讨论】:

【参考方案1】:

因此,从逻辑的角度来看,您的设置方式存在一些问题。我相信我已经解决了我在下面的代码中看到的所有问题。

只打印 0 的原因是因为 getLocation 函数中引用的 currentLatitude/currentLongitude/currentTimestamp 在间隔开始后周围有一个闭包,它不会刷新到新版本的函数这是在每次重新渲染时创建的。为了解决这个问题,我在所做的更改中从 getLocation 函数中删除了对这些变量的引用。

仍然输出 0 的原因是因为 getLocationInterval 在每次渲染中都设置为 undefined。您必须将其设为引用或状态变量才能使其在重新渲染之间保持不变。

我将 startStop 函数中的逻辑移到了 useEffect 钩子中,因为这是在钩子组件中产生效果的正确方法。它还允许将所有逻辑组合在一个简单的位置,如果您需要在其他位置使用它,可以很容易地将其提取到自定义钩子中。

const Tracking = props => 
const [currentLatitude, setCurrentLatitude] = useState(0);
const [currentLongitude, setCurrentLongitude] = useState(0);
const [currentTimestamp, setCurrentTimestamp] = useState(0);

const [isTracking, setIsTracking] = useState(false);

useEffect(() => 
    if (!isTracking) return;
    function getLocation() 
    navigator.geolocation.getCurrentPosition(
        position => 
        setCurrentLongitude(position.coords.longitude);
        setCurrentLatitude(position.coords.latitude);
        setCurrentTimestamp(position.timestamp);
        console.log(
            position.coords.longitude,
            position.coords.latitude,
            position.timestamp
        );
        ,
        error => alert(error.message),
         enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 
    );
    
    let getLocationInterval = setInterval(getLocation, 500);
    return () => clearInterval(getLocationInterval);
, [isTracking]);

return (
    <View style= width: '100%', height: '100%' >
    <MapView showsUserLocation style= flex: 1  />
    <MenuButton
        title=isTracking ? 'Stop' : 'Start'
        onPress=() => 
        setIsTracking(!isTracking);
        
    />
    </View>
);
;

编辑:useEffect 返回的解释:

上面代码第27行的返回函数:return () =&gt; clearInterval(getLocationInterval);

clearInterval 不会立即运行,因为它是箭头函数声明的一部分。我们返回那个函数而不是直接调用它。一旦我们返回函数声明,react 现在可以再次控制代码并在需要时调用它。

特别是,useEffect 允许您返回一个用于清理的函数。它会在组件卸载时或再次运行钩子之前调用该函数(即依赖数组已更改)

React 官方文档还有一点信息:https://reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect

【讨论】:

哇,这很完美。太感谢了。将花几分钟时间了解您对我的最初为什么不起作用的解释,然后可能会有一个后续问题要问您!再次感谢。 您能否解释一下为什么在第 27 行,清除了间隔,为什么没有在设置后立即清除该间隔?显然这很好,因为这是我想要它做的。但是为什么return 不会在let getLocationInterval 行运行后立即被调用,从而阻止函数被重复调用? (对于 cmet 之间的延迟,我很抱歉,最近几周我一直在做其他事情) 我添加了有关清理功能的更多信息

以上是关于在本地反应中使用带有地理定位的钩子/状态我哪里出错了?的主要内容,如果未能解决你的问题,请参考以下文章

为啥反应钩子使用状态显示错误消息

地理定位 API 在 android 中以本地语言显示地址

获得具有反应本地地理位置的城市?

将本地共享地理位置与其他设备进行反应

在表单控制输入中反应 js useReducer 钩子

如何在反应查询中存储本地状态?