[Bug]: dense onCameraChanged traffic during gestures can overwhelm JS without a native throttle
Nobody has claimed this yet.
- Dominant language
- Kotlin
- Stars
- 2.9k
- Forks
- 947
- Avg merge
- 6d 37m
- Merged PRs (30d)
- 1
Description
Mapbox Version
11.20.1
React Native Version
0.83.2
Platform
- iOS
- Android
@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.
onCameraChangedfires 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
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 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
0and preserves current behavior when omitted. - The API shape is consistent with the existing debounce/timing-related
MapViewprops. - A small patch that adds
cameraChangedThrottleInterval?: numberon the JS side and throttles nativecameraChangeddispatch 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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the MapView JavaScript prop surface and the native cameraChanged dispatch paths on Android and iOS, comparing existing debounce or timing-related MapView props. Use the supplied dense-points example during pinch-zoom to verify that a default interval of 0 preserves current behavior, throttling reduces event volume when configured, and onMapIdle still reports the settled camera state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, kotlin, react-native
- Domain
- mobile, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100