rnmapbox / rnmapbox/maps

[Bug]: dense onCameraChanged traffic during gestures can overwhelm JS without a native throttle

Open
#4,192 3 comments 0 reactions 0 assignees View on GitHub

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

  1. Render a map with a dense source and attach non-trivial JS work to onCameraChanged.
  2. Pinch-zoom repeatedly.
  3. onCameraChanged fires on every native camera tick on both platforms.
  4. 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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.