React框架下使用Deck.GL组件时新增地图图层的正确方法
问题根因
Recoil的状态默认启用了不可变冻结,你存储在selMapLayersArray对应状态中的Deck.GL图层实例被Recoil冻结为只读对象。而Deck.GL内部渲染逻辑会主动修改传入的图层实例的context、state等内部属性,修改只读对象就会触发你遇到的TypeError。
最优实现方案
不要直接把完整的Deck.GL图层实例存储到Recoil中,你应该拆分「图层配置数据」和「图层实例创建逻辑」:
- 第一步:在Recoil中只存储图层的配置参数,而非实例化后的图层对象
你可以存储每个图层的类型、id、数据、样式配置这些可序列化的纯数据,示例如下:// recoil store 示例定义 export const stateLayerConfigList = atom({ key: 'stateLayerConfigList', default: [] // 存储格式参考:[{type: 'ScatterplotLayer', id: 'point-layer', data: [], opacity: 0.8}] }) - 第二步:在地图组件内根据Recoil存储的配置动态创建图层实例
拿到Recoil里的配置列表后,在组件渲染时动态实例化对应的Deck.GL图层,这样传入DeckGL组件的都是新创建的可写实例,不会触发只读报错:import { ScatterplotLayer, GeoJsonLayer } from '@deck.gl/layers' // 引入你实际用到的图层类 // 其余原有导入保持不变 const Map = () => { // 原有变量定义保持不变 const layerConfigList = useRecoilValue(stateLayerConfigList) // 读取配置而非图层实例 // 动态生成图层实例 const mapLayersArray = layerConfigList.map(config => { const {type, ...layerProps} = config switch(type) { case 'ScatterplotLayer': return new ScatterplotLayer(layerProps) case 'GeoJsonLayer': return new GeoJsonLayer(layerProps) // 补充你实际用到的其他图层类型 default: return null } }).filter(Boolean) // return部分的DeckGL组件传入layers={mapLayersArray}即可,其余逻辑保持不变 }
兜底方案:如果你确实有特殊需求要把图层实例存在Recoil中,可以在定义atom时关闭不可变冻结:
export const stateMapLayersArray = atom({ key: 'stateMapLayersArray', default: [], dangerouslyAllowMutability: true // 关闭冻结,允许修改状态内的对象 })该方案不符合Recoil的不可变状态设计原则,可能引发其他不可预期的渲染问题,非必要不建议使用。
内容的提问来源于stack exchange,提问作者Jesse Reilly
相关产品推荐
相关产品推荐

