Expo集成Firebase跨屏幕数据存储问题求助
嘿,我来帮你搞定这两个问题!咱们一个个来拆解:
users节点下 你现在的更新代码是直接对users/根节点执行update,这会把经纬度数据添加到users节点本身,而不是当前登录用户的UID子节点里。要解决这个,得先拿到当前登录用户的UID,然后定位到对应的路径。
解决方案:
首先,获取当前登录的Firebase用户,然后针对其UID节点做更新。你可以把更新逻辑放在位置变化的回调里(也就是watchPosition的第一个参数里),这样每次位置更新时,都会同步到Firebase:
修改你componentDidMount里的位置监听部分:
const watchId = navigator.geolocation.watchPosition( ({ coords: { latitude, longitude } }) => { this.setState({ latitude, longitude }, () => { // 状态更新完成后,同步到Firebase const currentUser = firebase.auth().currentUser; if (currentUser) { // 定位到当前用户的节点 firebase.database().ref(`users/${currentUser.uid}`).update({ latitude, longitude }).then(() => { console.log("经纬度已成功更新到Firebase"); }).catch(err => { console.error("更新失败:", err); }); } else { console.log("当前没有登录用户,无法更新位置"); } }); }, (error) => this.setState({ error: error.message }), { enableHighAccuracy: true, timeout: 0, maximumAge: 1000, distanceFilter: 1 }, );
这样一来,每次位置变化时,都会把经纬度存入users/[当前用户UID]节点下,而不是users根节点啦。
这个错误的原因是:当你的主屏幕组件被卸载(比如用户跳转到其他页面)后,geolocation.watchPosition还在持续监听位置变化,这时回调里的this.setState会尝试更新一个已经不存在的组件状态,所以就报错了。另外你代码里还有个小bug:catch块里的变量是err,但你打印的是error,这会导致控制台输出undefined。
解决方案:
- 在组件卸载时清除位置监听
给你的主屏幕组件添加componentWillUnmount生命周期方法,用来清理位置监听:
async componentWillUnmount() { // 从AsyncStorage里取出之前保存的watchId const watchId = await AsyncStorage.getItem('LiveFeedId'); if (watchId) { // 清除位置监听 navigator.geolocation.clearWatch(JSON.parse(watchId)); // 删除保存的watchId await AsyncStorage.removeItem('LiveFeedId'); } }
修复catch块的变量错误
把console.error(error);改成console.error(err);,不然会出现error is not defined的额外错误。额外优化:避免重复监听
在启动新的位置监听前,先检查是否已有旧的监听存在并清除,这样更健壮:
修改后的完整componentDidMount代码:
async componentDidMount() { try { let { status } = await Permissions.getAsync(Permissions.LOCATION); if (status !== 'granted') { const { status: newStatus } = await Permissions.askAsync(Permissions.LOCATION); if (newStatus !== 'granted') { this.setState({ error: "位置权限未获取,无法更新位置信息" }); return; } } // 先清除可能存在的旧监听 const oldWatchId = await AsyncStorage.getItem('LiveFeedId'); if (oldWatchId) { navigator.geolocation.clearWatch(JSON.parse(oldWatchId)); } const watchId = navigator.geolocation.watchPosition( ({ coords: { latitude, longitude } }) => { this.setState({ latitude, longitude }, () => { const currentUser = firebase.auth().currentUser; if (currentUser) { firebase.database().ref(`users/${currentUser.uid}`).update({ latitude, longitude }).catch(err => console.error("更新位置到Firebase失败:", err)); } }); }, (error) => this.setState({ error: error.message }), { enableHighAccuracy: true, timeout: 0, maximumAge: 1000, distanceFilter: 1 }, ); await AsyncStorage.setItem('LiveFeedId', JSON.stringify(watchId)); } catch (err) { console.error("获取位置权限失败:", err); this.setState({ error: err.message }); } }
额外提醒:
为了确保主屏幕组件只有在用户登录时才执行定位逻辑,你还可以添加Firebase auth状态监听,避免用户未登录时出现错误:
componentDidMount() { // 监听用户登录状态 this.authUnsubscribe = firebase.auth().onAuthStateChanged(user => { if (user) { // 用户已登录,初始化定位追踪 this.initLocationTracking(); } else { // 用户未登录,跳转到登录页 this.props.navigation.navigate('Login'); } }); } componentWillUnmount() { // 取消auth状态监听 if (this.authUnsubscribe) { this.authUnsubscribe(); } // 清除位置监听 // ...之前的清除代码 } // 把定位逻辑抽成单独方法 async initLocationTracking() { // 这里放之前的定位权限和监听代码 }
这样调整后,两个问题应该都能解决啦!
内容的提问来源于stack exchange,提问作者Neeraj Kumar

