React setState更新数组状态时提示无法添加新属性报错
问题描述
在项目中接入Geolocation定位服务实现位置持续监听时,将每次回调返回的坐标推入预先声明的数组变量,调用setState更新坐标列表状态时抛出cannot add new property错误。
原有问题代码如下:
let dataarray = [] const startTrackingFunction = async () => { await Geolocation.watchPosition( position => { const crd = position.coords; setPosition({ latitude: crd.latitude, longitude: crd.longitude, latitudeDelta: LATITUDE_DELTA, longitudeDelta: LONGITUDE_DELTA, }); dataarray.push(crd); setCoordinates(dataarray); }, error => { console.log(error); }, { showLocationDialog: true, enableHighAccuracy: true, timeout: 20000, maximumAge: 0, distanceFilter: 0, }); setTracking(true); }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
错误原因
- 违反React状态不可变原则:第一次调用
setCoordinates(dataarray)后,React会对传入的状态值做冻结处理(Object.freeze),禁止任何修改操作。后续位置更新触发回调时,代码仍对同一个dataarray数组引用执行push操作,本质是给已冻结的数组对象新增索引属性,直接触发cannot add new property报错。 - 接口误用:
Geolocation.watchPosition不是Promise类型接口,添加await没有实际等待效果,该方法会同步返回位置监听器的唯一ID,用于后续取消监听。 - 存储方式不合理:将坐标数组声明在组件外部的全局作用域,容易引发组件多实例数据污染、闭包取值异常等问题。
修复方案
移除外部全局数组变量,使用React状态的函数式更新做不可变数据修改,每次更新返回全新的数组引用,同时修正watchPosition的调用方式:
const startTrackingFunction = () => { // 同步获取监听器ID,用于后续停止定位时清除监听 const watchId = Geolocation.watchPosition( position => { const crd = position.coords; setPosition({ latitude: crd.latitude, longitude: crd.longitude, latitudeDelta: LATITUDE_DELTA, longitudeDelta: LONGITUDE_DELTA, }); // 基于上一次的坐标状态生成新数组,不修改原状态值 setCoordinates(prevCoords => [...prevCoords, crd]); }, error => { console.log(error); }, { showLocationDialog: true, enableHighAccuracy: true, timeout: 20000, maximumAge: 0, distanceFilter: 0, } ); setTracking(true); // 可将watchId存入ref/state,页面卸载或停止定位时调用 Geolocation.clearWatch(watchId) 避免内存泄漏 }
注意事项
- 所有React状态更新都要遵循不可变原则:数组更新使用展开运算符、
map/filter/concat等返回新数组的方法,对象更新使用展开运算符、Object.assign创建新对象,禁止直接修改原状态引用。 - 如果需要在组件内存储不触发重渲染的临时变量,使用
useRef而非组件外的全局变量,避免闭包和多实例冲突问题。
内容的提问来源于stack exchange,提问作者jessanKhan7
相关产品推荐
相关产品推荐

