在本地反应中使用带有地理定位的钩子/状态我哪里出错了?
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 () => clearInterval(getLocationInterval);
clearInterval 不会立即运行,因为它是箭头函数声明的一部分。我们返回那个函数而不是直接调用它。一旦我们返回函数声明,react 现在可以再次控制代码并在需要时调用它。
特别是,useEffect
允许您返回一个用于清理的函数。它会在组件卸载时或再次运行钩子之前调用该函数(即依赖数组已更改)
React 官方文档还有一点信息:https://reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect
【讨论】:
哇,这很完美。太感谢了。将花几分钟时间了解您对我的最初为什么不起作用的解释,然后可能会有一个后续问题要问您!再次感谢。 您能否解释一下为什么在第 27 行,清除了间隔,为什么没有在设置后立即清除该间隔?显然这很好,因为这是我想要它做的。但是为什么return
不会在let getLocationInterval
行运行后立即被调用,从而阻止函数被重复调用? (对于 cmet 之间的延迟,我很抱歉,最近几周我一直在做其他事情)
我添加了有关清理功能的更多信息以上是关于在本地反应中使用带有地理定位的钩子/状态我哪里出错了?的主要内容,如果未能解决你的问题,请参考以下文章