Mapbox Version
11.20.1
React Native Version
0.83.2
Platform
@rnmapbox/maps version
10.3.0
Standalone component to reproduce
import React, { useMemo, useState } from 'react';
import { Camera, CircleLayer, MapView, ShapeSource } from '@rnmapbox/maps';
function buildPoints(count) {
return {
type: 'FeatureCollection',
features: Array.from({ length: count }, (_, index) => {
const row = Math.floor(index / 100);
const column = index % 100;
return {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [12.2 + column * 0.01, 41.6 + row * 0.01],
},
properties: { id: String(index) },
};
}),
};
}
export default function BugReportExample() {
const [cameraChangedCount, setCameraChangedCount] = useState(0);
const [mapIdleCount, setMapIdleCount] = useState(0);
const shape = useMemo(() => buildPoints(5000), []);
return (
<MapView
style={{ flex: 1 }}
onCameraChanged={() => {
setCameraChangedCount((count) => count + 1);
}}
onMapIdle={() => {
setMapIdleCount((count) => count + 1);
}}
>
<Camera centerCoordinate={[12.4964, 41.9028]} zoomLevel={7} />
<ShapeSource id="dense-points" shape={shape}>
<CircleLayer
id="dense-point-layer"
style={{
circleRadius: 3,
circleColor: '#ff4d4d',
}}
/>
</ShapeSource>
</MapView>
);
}
Observed behavior and steps to reproduce
- Render a map with a dense source and attach non-trivial JS work to
onCameraChanged.
- Pinch-zoom repeatedly.
onCameraChanged fires on every native camera tick on both platforms.
- Even if the heavy work is app-side, there is currently no native throttle for this event, so gesture-driven zoom can overwhelm the JS thread faster than
onMapIdle can settle.
In practice this shows up as zoom stutter in apps that recompute visible data, declutter symbols, or trigger viewport-based fetches during camera changes.
Expected behavior
There should be an opt-in way to reduce onCameraChanged event volume natively while keeping the default behavior unchanged for existing consumers.
A cameraChangedThrottleInterval prop on MapView would let apps trade off intermediate camera granularity against JS load, while still using onMapIdle for the final settled camera state.
Notes / preliminary analysis
- This is not a request to change the default event semantics.
- The change is backward compatible if the prop defaults to
0 and preserves current behavior when omitted.
- The API shape is consistent with the existing debounce/timing-related
MapView props.
- A small patch that adds
cameraChangedThrottleInterval?: number on the JS side and throttles native cameraChanged dispatch on Android and iOS appears sufficient.
Additional links and references
- A PR with an opt-in implementation and example update will be linked from this issue.
Mapbox Version
11.20.1
React Native Version
0.83.2
Platform
@rnmapbox/mapsversion10.3.0
Standalone component to reproduce
Observed behavior and steps to reproduce
onCameraChanged.onCameraChangedfires on every native camera tick on both platforms.onMapIdlecan settle.In practice this shows up as zoom stutter in apps that recompute visible data, declutter symbols, or trigger viewport-based fetches during camera changes.
Expected behavior
There should be an opt-in way to reduce
onCameraChangedevent volume natively while keeping the default behavior unchanged for existing consumers.A
cameraChangedThrottleIntervalprop onMapViewwould let apps trade off intermediate camera granularity against JS load, while still usingonMapIdlefor the final settled camera state.Notes / preliminary analysis
0and preserves current behavior when omitted.MapViewprops.cameraChangedThrottleInterval?: numberon the JS side and throttles nativecameraChangeddispatch on Android and iOS appears sufficient.Additional links and references