React Native中使用GeoFire在Firebase存储当前位置遇问题
解决React Native中GeoFire存储用户位置时的"TypeError: Attempted to assign to readonly property"问题
问题核心分析
你碰到的这个错误,大概率是因为GeoFire实例的创建方式不对——从你的代码看,直接调用geofire(firebaseRef)是错误的操作,GeoFire需要通过new关键字来实例化对象,这种不规范的实例化方式会触发只读属性赋值的报错。另外也可能和React Native环境下Firebase引用的处理逻辑有关,毕竟之前Android能正常运行,可能是旧版本依赖的宽松检查掩盖了这个问题。
分步修复方案
1. 先修正GeoFire的实例化方式
这是最关键的一步,一定要用new关键字创建GeoFire实例,同时确保指向的是已认证用户专属的位置节点(避免数据混乱):
import firebase from '@react-native-firebase/app'; import database from '@react-native-firebase/database'; import GeoFire from 'geofire'; // 先获取当前已认证用户的ID const currentUserId = firebase.auth().currentUser?.uid; if (!currentUserId) { console.error('请先让用户完成认证'); return; } // 创建指向该用户位置节点的Firebase引用 const userLocationRef = database().ref(`Users/${currentUserId}/location`); // 正确实例化GeoFire const geofireUser = new GeoFire(userLocationRef);
2. 完整的位置获取与存储示例
下面是包含权限请求、位置获取、GeoFire存储的完整流程代码,适配React Native环境:
import { PermissionsAndroid, Platform } from 'react-native'; import firebase from '@react-native-firebase/app'; import database from '@react-native-firebase/database'; import GeoFire from 'geofire'; import Geolocation from '@react-native-community/geolocation'; // 封装位置权限请求函数 const requestLocationPermission = async () => { if (Platform.OS === 'android') { const granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, { title: '应用需要位置权限', message: '为了更新您的位置信息,请允许应用访问您的位置', buttonPositive: '允许', buttonNegative: '取消', }, ); return granted === PermissionsAndroid.RESULTS.GRANTED; } // iOS权限可根据需求单独处理,这里默认返回true return true; }; // 存储用户位置到GeoFire的主函数 const saveUserCurrentLocation = async () => { // 先检查权限 const hasPermission = await requestLocationPermission(); if (!hasPermission) { console.error('未获取到位置权限,无法存储位置'); return; } // 确认用户已认证 const currentUserId = firebase.auth().currentUser?.uid; if (!currentUserId) { console.error('用户未登录,请先完成认证'); return; } // 获取当前设备位置 Geolocation.getCurrentPosition( async (position) => { const { latitude, longitude } = position.coords; // 创建GeoFire实例 const userLocationRef = database().ref(`Users/${currentUserId}/location`); const geofireUser = new GeoFire(userLocationRef); // 存储位置到GeoFire try { await geofireUser.set(currentUserId, [latitude, longitude]); console.log('用户位置已成功存储到GeoFire'); } catch (error) { console.error('存储位置失败:', error); } }, (error) => { console.error('获取设备位置失败:', error.message); }, { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }, ); }; // 调用函数执行流程 saveUserCurrentLocation();
3. 额外排查点
- Firebase数据库权限:确保你的Realtime Database规则允许已认证用户写入自己的位置节点,示例规则如下:
{ "rules": { "Users": { "$userId": { "location": { ".read": "auth.uid === $userId", ".write": "auth.uid === $userId" } } } } } - 依赖版本兼容性:检查
geofire、@react-native-firebase/database和React Native的版本是否匹配,建议使用最新稳定版,避免版本冲突导致的奇怪报错。 - 只读属性修改:排查代码中是否有直接修改GeoFire实例内部属性的操作——GeoFire的内部属性是只读的,只能通过它提供的
set、get等API来操作数据。
关于之前Android正常的说明
可能是旧版本的GeoFire或Firebase依赖在Android环境下对实例化方式的检查不严格,没有触发报错,但在更新后的依赖或其他平台(比如iOS)下,严格的类型检查就暴露了这个问题。
内容的提问来源于stack exchange,提问作者Bisclavret
相关产品推荐
相关产品推荐

