React Native如何在外部回调中修改组件状态并更新位置显示
问题描述
使用@mauron85/react-native-background-geolocation库时,将后台定位启动函数定义在App组件外部后,无法在BackgroundGeolocation.on('location')回调中修改组件内的loc状态变量,希望位置更新时能实时在屏幕显示最新位置。
用户代码:
import React, {useState} from 'react'; import BackgroundGeolocation, { Location, } from '@mauron85/react-native-background-geolocation'; const startBackgroundService = () => { BackgroundGeolocation.configure({ desiredAccuracy: BackgroundGeolocation.HIGH_ACCURACY, stationaryRadius: 50, distanceFilter: 10, notificationTitle: 'Strings.Geolocation.NOTIFICATION_TITLE', notificationText: 'Strings.Geolocation.NOTIFICATION_DESCRIPTION', debug: false, startOnBoot: false, stopOnTerminate: true, locationProvider: BackgroundGeolocation.ACTIVITY_PROVIDER, interval: 1000, fastestInterval: 10000, activitiesInterval: 30000, stopOnStillActivity: false, notificationIconColor: '#4CAF50', notificationIconSmall: 'notification_icon', }); BackgroundGeolocation.on('authorization', status => { if (status !== BackgroundGeolocation.AUTHORIZED) { } else if (status.hasPermissions) { } }); BackgroundGeolocation.on('location', location => { BackgroundGeolocation.startTask(async taskKey => { console.log(location.longitude); console.log(location.latitude); // 需要在这里修改App组件中的loc状态 console.log(new Date()); BackgroundGeolocation.endTask(taskKey); }); }); BackgroundGeolocation.start(); }; startBackgroundService(); export default function App() { let [loc, setLoc] = useState('t'); // 需要显示从BackgroundGeolocation回调中更新后的loc return ( <SafeAreaView style={styles.container}> <Text>{loc}</Text> <Button title={'q'} /> </SafeAreaView> ); }
解决方案
方案1:将状态更新函数传入启动服务函数
把组件的setLoc作为参数传给外部的startBackgroundService,让定位回调能直接调用它更新状态:
import React, {useState, useEffect} from 'react'; import {SafeAreaView, Text, Button, StyleSheet} from 'react-native'; import BackgroundGeolocation, { Location, } from '@mauron85/react-native-background-geolocation'; const startBackgroundService = (updateLocation) => { BackgroundGeolocation.configure({ desiredAccuracy: BackgroundGeolocation.HIGH_ACCURACY, stationaryRadius: 50, distanceFilter: 10, notificationTitle: 'Strings.Geolocation.NOTIFICATION_TITLE', notificationText: 'Strings.Geolocation.NOTIFICATION_DESCRIPTION', debug: false, startOnBoot: false, stopOnTerminate: true, locationProvider: BackgroundGeolocation.ACTIVITY_PROVIDER, interval: 1000, fastestInterval: 10000, activitiesInterval: 30000, stopOnStillActivity: false, notificationIconColor: '#4CAF50', notificationIconSmall: 'notification_icon', }); BackgroundGeolocation.on('authorization', status => { if (status !== BackgroundGeolocation.AUTHORIZED) { // 处理未授权逻辑 } else if (status.hasPermissions) { // 处理已授权逻辑 } }); BackgroundGeolocation.on('location', location => { BackgroundGeolocation.startTask(async taskKey => { // 格式化位置信息 const newLoc = `纬度: ${location.latitude}, 经度: ${location.longitude}`; updateLocation(newLoc); // 调用状态更新函数 console.log(location.longitude, location.latitude); console.log(new Date()); BackgroundGeolocation.endTask(taskKey); }); }); BackgroundGeolocation.start(); }; export default function App() { const [loc, setLoc] = useState('等待定位...'); useEffect(() => { // 组件挂载时启动服务,传入setLoc startBackgroundService(setLoc); // 组件卸载时停止服务、清理监听 return () => { BackgroundGeolocation.stop(); BackgroundGeolocation.removeAllListeners(); }; }, []); return ( <SafeAreaView style={styles.container}> <Text>{loc}</Text> <Button title={'q'} /> </SafeAreaView> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, });
方案2:将服务初始化移到组件内部
直接在组件的useEffect中配置和启动定位服务,这样可以直接访问组件内的setLoc:
import React, {useState, useEffect} from 'react'; import {SafeAreaView, Text, Button, StyleSheet} from 'react-native'; import BackgroundGeolocation, { Location, } from '@mauron85/react-native-background-geolocation'; export default function App() { const [loc, setLoc] = useState('等待定位...'); useEffect(() => { // 配置后台定位 BackgroundGeolocation.configure({ desiredAccuracy: BackgroundGeolocation.HIGH_ACCURACY, stationaryRadius: 50, distanceFilter: 10, notificationTitle: 'Strings.Geolocation.NOTIFICATION_TITLE', notificationText: 'Strings.Geolocation.NOTIFICATION_DESCRIPTION', debug: false, startOnBoot: false, stopOnTerminate: true, locationProvider: BackgroundGeolocation.ACTIVITY_PROVIDER, interval: 1000, fastestInterval: 10000, activitiesInterval: 30000, stopOnStillActivity: false, notificationIconColor: '#4CAF50', notificationIconSmall: 'notification_icon', }); // 监听授权状态 const authListener = BackgroundGeolocation.on('authorization', status => { if (status !== BackgroundGeolocation.AUTHORIZED) { // 处理未授权逻辑 } else if (status.hasPermissions) { // 处理已授权逻辑 } }); // 监听位置更新并修改状态 const locationListener = BackgroundGeolocation.on('location', location => { BackgroundGeolocation.startTask(async taskKey => { const newLoc = `纬度: ${location.latitude}, 经度: ${location.longitude}`; setLoc(newLoc); console.log(location.longitude, location.latitude); console.log(new Date()); BackgroundGeolocation.endTask(taskKey); }); }); // 启动定位服务 BackgroundGeolocation.start(); // 组件卸载时清理资源 return () => { BackgroundGeolocation.stop(); authListener.remove(); locationListener.remove(); BackgroundGeolocation.removeAllListeners(); }; }, []); return ( <SafeAreaView style={styles.container}> <Text>{loc}</Text> <Button title={'q'} /> </SafeAreaView> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, });
关键注意点
- 两种方案都解决了外部函数无法访问组件状态的问题,核心是让定位回调能获取到
setLoc函数。 - 必须在组件卸载时停止定位服务并移除所有监听,避免内存泄漏和不必要的资源消耗。
- 补充了原代码中缺失的组件导入和样式定义,避免运行报错。
内容的提问来源于stack exchange,提问作者Alexandr
相关产品推荐
相关产品推荐

