@@ -30,6 +30,16 @@ function MarkersLayerInner({ locations }: MarkersLayerProps) {
3030 const popupRef = useRef < maplibregl . Popup | null > ( null )
3131 const hoveredFeatureIdRef = useRef < number | null > ( null )
3232 const settingUpRef = useRef ( false )
33+ const willUnmountRef = useRef ( false )
34+
35+ // Track actual unmount so we only tear down layers/source on final unmount,
36+ // not on every dependency change (e.g. locations data update).
37+ // MUST be declared BEFORE the main effect so its cleanup runs first on unmount.
38+ useEffect ( ( ) => {
39+ return ( ) => {
40+ willUnmountRef . current = true
41+ }
42+ } , [ ] )
3343
3444 // 字符串 location.id → 数字 feature index 映射
3545 const idToIndexMap = useMemo ( ( ) => {
@@ -125,19 +135,23 @@ function MarkersLayerInner({ locations }: MarkersLayerProps) {
125135 if ( e . features && e . features [ 0 ] ) {
126136 const fid = e . features [ 0 ] . id as number
127137 if ( hoveredFeatureIdRef . current === fid ) return
128- // 清除上一个
138+ // 清除上一个(try-catch 防止 source 在检查和调用之间被移除)
129139 if ( hoveredFeatureIdRef . current !== null ) {
130- map . setFeatureState (
131- { source : 'locations' , id : hoveredFeatureIdRef . current } ,
132- { hover : false }
133- )
140+ try {
141+ map . setFeatureState (
142+ { source : 'locations' , id : hoveredFeatureIdRef . current } ,
143+ { hover : false }
144+ )
145+ } catch ( e ) { if ( e instanceof Error && ! e . message . includes ( 'does not exist' ) ) console . warn ( 'Feature state error:' , e ) }
134146 }
135147 // 设置新的
136148 hoveredFeatureIdRef . current = fid
137- map . setFeatureState (
138- { source : 'locations' , id : fid } ,
139- { hover : true }
140- )
149+ try {
150+ map . setFeatureState (
151+ { source : 'locations' , id : fid } ,
152+ { hover : true }
153+ )
154+ } catch ( e ) { if ( e instanceof Error && ! e . message . includes ( 'does not exist' ) ) console . warn ( 'Feature state error:' , e ) }
141155 // 同步 store(侧边栏卡片高亮)
142156 const locId = e . features [ 0 ] . properties ?. id as string
143157 if ( locId ) {
@@ -278,7 +292,9 @@ function MarkersLayerInner({ locations }: MarkersLayerProps) {
278292 popupRef . current . remove ( )
279293 popupRef . current = null
280294 }
281- // 清除图层和数据源(如果还存在)
295+ // Only remove layers/source on actual unmount, not on dependency changes.
296+ // On data updates, setupLayers() handles incremental updates via setData().
297+ if ( ! willUnmountRef . current ) return
282298 try {
283299 if ( map . getLayer ( 'location-labels' ) ) map . removeLayer ( 'location-labels' )
284300 if ( map . getLayer ( 'location-dots' ) ) map . removeLayer ( 'location-dots' )
@@ -386,7 +402,7 @@ function renderPopupHTML(props: Record<string, unknown>): string {
386402 const visitHtml =
387403 visitCount != null && visitCount > 0
388404 ? `<span style="font-size:11px;font-weight:600;color:var(--color-text, #1a1a2e);opacity:0.7;flex-shrink:0;">
389- 🔥 ${ visitCount >= 1000 ? `${ ( visitCount / 10000 ) . toFixed ( 1 ) } 万` : visitCount }
405+ 🔥 ${ visitCount >= 10000 ? `${ ( visitCount / 10000 ) . toFixed ( 1 ) } 万` : visitCount }
390406 </span>`
391407 : ''
392408
0 commit comments