[Bug]: `onMapIdle` and `onRegionDidChange` events do not work
Mapbox Implementation
Mapbox
Mapbox Version
10.1.38
React Native Version
0.76.9
Platform
iOS
@rnmapbox/maps version
default
Standalone component to reproduce
import React from 'react';
import { StyleSheet, View } from 'react-native';
import Mapbox, { MapView } from '@rnmapbox/maps';
const Map = () => {
return (
<View style={styles.container}>
<MapView
style={styles.map}
onMapIdle={() => console.log('map idle')}
onRegionDidChange={() => console.log('region did change')}
/>
</View>
);
};
export default Map;
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
flex: 1,
},
});
Observed behavior and steps to reproduce
- Launch the app using the component above.
- Let the map fully load.
- Try interacting with it by zooming or dragging.
- Notice that neither onMapIdle nor onRegionDidChange trigger initially.
I found that if any state change occurs after the map finishes loading (e.g., inside onDidFinishLoadingMap), then onRegionDidChange starts working as expected. onMapIdle still never triggers, regardless of state changes.
So there seems to be some internal dependency on state/context updates before events begin firing properly — and onMapIdle seems completely non-functional.
Expected behavior
onRegionDidChange should trigger whenever the map's visible region changes — either via user interaction or camera animation.
onMapIdle should fire once the map settles after a movement or animation.
Notes / preliminary analysis
No warnings or errors in the console.
Map loads and renders correctly otherwise.
I’ve temporarily added a dummy state update in the onDidFinishLoadingMap event to "kickstart" the event system, and this made onRegionDidChange work:
const [mapReady, setMapReady] = useState(false);
<MapView onDidFinishLoadingMap={() => {
setMapReady(true); // Temporary workaround
}} />
This is not ideal, but confirms there might be an internal state issue blocking events from firing until something triggers a re-render.
Additional links and references
Minimal repro repo: https://github.com/sabuhiteymurov/rnmapbox-on-map-idle-reproducer
I'm seeing this bug as well. It's very annoying.
Same issue
Same issue. I turned off the new architecture and its working.
same issue
We encountered this issue last year on iOS where onMapIdle would work initially, but would stop working after a while.
I wrote a workaround that listens for the touch-up event, then fetches the current map state manually. It preserves the current onMapIdle API structure so it just slots in. It doesn't handle imperative map movements so you'll need to add additional logic to handle those.
import { Position } from '@rnmapbox/maps/lib/typescript/src/types/Position';
const mapRef = useRef<MapView | null>(null);
const currentCameraPosition = useRef<GeoJSON.Point['coordinates'] | null>(null);
async function getMapState(center?: Position | null, zoom?: number | null, bounds?: [Position, Position] | null): Promise<MapboxGL.MapState | null> {
if (!mapRef.current) return null;
const [_center, _zoom, _bounds] = await Promise.all([
center || mapRef.current.getCenter(),
zoom || mapRef.current.getZoom(),
bounds || mapRef.current.getVisibleBounds(),
]);
return normalizeMapState(_center, _zoom, _bounds);
}
function normalizeMapState(center: Position, zoom: number, bounds: [Position, Position]): MapboxGL.MapState {
return {
properties: {
center,
bounds: { ne: bounds[0], sw: bounds[1] },
zoom,
heading: -1,
pitch: -1,
},
gestures: { isGestureActive: true },
timestamp: Date.now(),
};
}
return (
<MapView
ref={mapRef}
onTouchEnd={async () => {
const mapState = await getMapState();
if (mapState && !_.isEqual(currentCameraPosition.current, mapState.properties.center) {
currentCameraPosition.current = mapState!.properties.center;
onMapMove?.(mapState!);
}
}}
>
// map components
</MapView>
);
Please fix this problem!!! We can not really use the library without having event listeners in place!
Can you give https://www.npmjs.com/package/@rnmapbox/maps/v/10.1.43-rc.0 a try? It fixes a race condition on iOS, mostly affecting new architecture
Still seeing this issue with:
@rnmapbox/maps: 10.1.43RNMapboxMapsVersion: 11.14.4- or default v10
newArchEnabled: truereact-native: 0.76.9expo: 52.0.47
@mgray88 you’ve tried the simple component in the report?!
I have managed to upgrade to Expo 53 in the meantime, so the new environment is:
- @rnmapbox/maps: 10.1.43
- RNMapboxMapsVersion: 11.14.4
- or default v10
- newArchEnabled: true
- react-native: 0.79.6
- expo: 53.0.22
Since the MapView component in the report had no children, I started with that and yes I could receive both onMapIdle and onRegionDidChange events (though the onRegionDidChange events were delayed by a full second). Adding back in components I needed, it turns out the LocationPuck for me caused onMapIdle and onRegionDidChange to no longer be called.
ETA: It seems to be specifically the pulsing property that prevents the events from being called
@mfazekas This bugs is back in 10.1.44 :(
@flessard have you tried the component in this bug report? Can you be as specific as possible?!
@mfazekas I’m trying to understand. The problem isn’t constant; it depends on where I am on the map. Sometimes the event is triggered, sometimes not. I made a short video showing the issue. In my video, when I first enter the map, onMapIdle is not triggered. If I move the map, it still doesn’t get triggered. If I zoom out, then onMapIdle eventually gets triggered. But if I zoom in, onMapIdle stops being triggered. I don’t know why. Do you have any idea? bug movie