为什么我的订阅方法不使用新状态,而仅使用初始状态?

时间:2020-10-17 19:38:01

标签: reactjs

我想在我的gps应用中计算速度,为此,我要使用状态来保存当前的前一个时间,纬度和经度。 我在useEffect中初始化了订阅函数,但是先前的时间状态lat和lon总是相同的,即使应该更新几次。如何在不使用localStorage保留这些值的情况下完成这项工作?

 const [lat, setLat] = useState(51.55);
    const [lon, setLon] = useState(0);
    const [prevLat, setPrevLat] = useState(0);
    const [prevLon, setPrevLon] = useState(0);
    const [time, setTime] = useState(new Date());
    const [prevTime, setPrevTime] = useState(new Date());

    useEffect(() => {
        Geolocation.getCurrentPosition().then((resp) => {

            setTime(resp.timestamp);
            setLat(resp.coords.latitude)
            setLon(resp.coords.longitude)

        }).catch((error) => {
            console.log('Error getting location', error);
        })

        const onSuccess = () => {
            console.log('success')
        }
        const onError = () => {
            console.log('error')
        }

        let watch = Geolocation.watchPosition(onSuccess, onError, { enableHighAccuracy: true });

        watch.subscribe((data) => {
            if (data && data.coords && data.coords.latitude && data.coords.longitude) {
                setPrevLat(lat);
                setPrevLon(lon);
                setPrevTime(time);
                setLat(data.coords.latitude);
                setLon(data.coords.longitude);
                setTime(data.timestamp);

                //calculate speed
                console.log(calculateSpeed(prevTime, prevLat, prevLon, data.timestamp, data.coords.latitude, data.coords.longitude));
            } else {
                alert(data.message, ' ', data.code)
            }

        });
    }, [])

换句话说,calculateSpeed的第4、5和6个参数是正确的值,但是prevTime,prevLon和prevLat仍然是相应的:应用程序开始运行的日期,0、0

1 个答案:

答案 0 :(得分:2)

这是经典的函数式编程问题,您必须使用闭包来提防。

发生了什么事?

  1. 您的useEffect仅在第一个渲染器上被调用,因为您有[]作为依赖项。
  2. 它使用匿名函数初始化watch.subscribe并创建一个 closure !该闭包可以访问父作用域变量,但是在创建闭包时会获取这些变量的快照!
  3. 触发subscribe回调后,它仍使用与创建闭包时相同的变量进行操作。这是"stale closure"

那么,如何解决这个问题?

一种策略是在状态更改时通过将状态变量传递给useEffect依赖项来取消订阅您的订阅回调并使用新的闭包重新订阅(但这将取决于Geolocation API) 。有关其他选项,请参见上面的链接。

通过使用带有类方法的类组件来替换watch.subscribe调用中的函数,您还可以轻松解决该问题。类方法没有纯函数式React钩子那样的过时的关闭问题。

我无法为您的问题提供具体的解决方案,因为设计更改可能会更改所需的行为,但是我希望这可以使您对问题有所了解。祝你好运!