捕获块无法捕获在我的自定义React钩子中引发的错误

时间:2020-09-02 18:20:27

标签: react-native error-handling async-await react-hooks

我有一个使用用户位置的应用程序。使用Expo的位置库,这就是我的代码的结构。

在主屏幕中,我创建一个回调函数并包装在useCallback HOC中。

const userLocationCallback = useCallback(async (location) => {
    // if we don't have any locations....fetch them and then sort them
    if (!businessLocations || businessLocations.length <= 0) {
        // todo check for wifi connectivity or data
        let fetchedLocations = await fetchAllLocations();
        sortByClosestLocation(location, fetchedLocations, updateLocations)
        if (fetchedLocations.length >= 5) {
            setListOfClosest(getFirstFromList(5, businessLocations))
        }
    } else {
        sortByClosestLocation(location, businessLocations, updateLocations)
        if (businessLocations.length >= 5) {
            setListOfClosest(getFirstFromList(5, businessLocations))
        }
    }
}, [businessLocations, locationServicesOn, permissionGranted]);

每次更新时,都会使用用户的位置调用此代码。该代码中唯一真正重要的部分是对 await fetchAllLocations 的调用。这是对返回JSON的服务器的api调用。

这是自定义钩子的代码:

export default (
    shouldTrackLocation,
    timeBetweenUpdates,
    callback,
    locationServicesOn,
    permissionGranted) => {

    const [locationError, setLocationError] = useState(null);

    let time = 0;

    useEffect(() => {

        let subscriber;
        const startWatching = async () => {
            try {
                subscriber = await watchPositionAsync({
                        accuracy: Accuracy.BestForNavigation,
                        // timeInterval and distanceInterval seem to have no effect, but you must have distanceInterval === 0
                        timeInterval: 10000,
                        distanceInterval: 0
                    }, (location) => {
                        if (time === 0 || new Date().getTime() >= time + timeBetweenUpdates) {

                            callback(location).catch(e => {
                                console.log("Location Error: ", "caught the error here")
                                throw e;
                            });
                            time = new Date().getTime();
                        }
                    }
                );
            } catch (e) {
                console.log("Location Error: ", "caught error in catch block")
                setLocationError(e);
            }
        };

        if (shouldTrackLocation && locationServicesOn && permissionGranted) {
            startWatching().catch(err => {
                console.log(err);
                subscriber?.remove();
                subscriber = null
            })
        } else {
            subscriber?.remove();
            subscriber = null;
        }

        return () => {
            subscriber?.remove();
        }
    }, [shouldTrackLocation, callback, locationServicesOn, permissionGranted]);

    return [locationError];
} 

我定义了一个函数startWatching,它使用了expo-location的函数watchPositionAsync。与代码相关的部分是回调函数(传递给watchPositionAsync的第二个参数)。回调函数是我在顶部显示的代码-我包装在useCallback中的代码。在主屏幕上(定义了此回调函数的地方),我像这样调用该钩子:

const [locationError] = useCurrentLocation(
        screenIsFocused,
        3000,
        userLocationCallback,
        locationServicesOn,
        permissionGranted,
    );

这是问题所在。如果回调函数生成错误,我想抓住它。我正在进行api调用,但是,如果服务器关闭,并且由于关闭而导致回调引发错误,为什么它没有显示在startWatching函数的catch块中?

如您所见,我尝试通过将.catch(e => ....)附加到调用的末尾来立即捕获它。那行得通,但是当我在该块中抛出错误时,它再也不会碰到我试图调用setLocationError(e)的下面的catch块。

我似乎无法理解我的错误流程。如果应用程序无法连接到服务器,我想做些事情。帮助我了解为什么会跳过catch块。

1 个答案:

答案 0 :(得分:0)

对于您的特定情况,您可以如下修改您的自定义:

  export default (
        shouldTrackLocation,
        timeBetweenUpdates,
        callback,
        locationServicesOn,
        permissionGranted) => {
    
        const [locationError, setLocationError] = useState(null);
    
        let time = 0;
    
        const innerLocationCallback = useCallback(async (location) => {       
            try {
                if (time === 0 || new Date().getTime() >= time + timeBetweenUpdates) {
                    callback(location);
                    time = new Date().getTime();
                }
            }
            catch (e) {
                setLocationError(e);
            }
        }, []);
    
    
        useEffect(() => {
            let subscriber;
            const startWatching = async () => {
                try {
                    subscriber = await watchPositionAsync({
                            accuracy: Accuracy.BestForNavigation,
                            timeInterval: 10000,
                            distanceInterval: 0
                        }, innerLocationCallback
                    );
                } catch (e) {
                    console.log("Location Error: ", "caught error in catch block")
                    setLocationError(e);
                }
            };
    
            if (shouldTrackLocation && locationServicesOn && permissionGranted) {
                startWatching().catch(err => {
                    console.log(err);
                    subscriber?.remove();
                    subscriber = null
                })
            } else {
                subscriber?.remove();
                subscriber = null;
            }
    
            return () => {
                subscriber?.remove();
            }
        }, [shouldTrackLocation, callback, locationServicesOn, permissionGranted]);
    
        return [locationError];
    }

我不想事先对您的代码进行重大更改,因为它有点复杂。 但是我个人不会这样调用我的外部回调。如果这对您有用,我可以稍后再回答。