[New Architecture][iOS] Changing a Polygon's zIndex makes the overlay disappear (Fabric reorder detaches the GMSPolygon)
Summary
On Fabric / iOS with provider="google", changing an overlay's zIndex causes that overlay (and sometimes others) to vanish from the map. When two overlays change zIndex in the same commit, the demoted one can also keep a stale fillColor (its prop update is dropped).
Environment
- react-native-maps: 1.27.2
- Platform: iOS,
provider="google" - New Architecture (Fabric): enabled
- React Native 0.83.x / Expo SDK 55
Reproduction
Render two overlapping polygons; on press, raise the pressed one's zIndex:
<MapView provider="google" style={{ flex: 1 }} initialRegion={/* ... */}>
<Polygon coordinates={triA} fillColor="rgba(255,0,0,0.4)" zIndex={selected === 'a' ? 2 : 1} onPress={() => setSelected('a')} tappable />
<Polygon coordinates={triB} fillColor="rgba(0,0,255,0.4)" zIndex={selected === 'b' ? 2 : 1} onPress={() => setSelected('b')} tappable />
</MapView>Tapping a polygon (which changes its zIndex) makes it disappear; tapping the other can leave the first stuck on its previous fill color.
Root cause
A zIndex change makes Fabric reorder the map's overlay children, which it does by unmounting and re-mounting the same view. react-native-maps treats that unmount as a removal: RNMapsGooglePolygonView didRemoveFromMap detaches the GMSPolygon (_view.map = nil). The immediate re-mount doesn't reliably re-attach, and a pure reorder doesn't re-deliver props — so the overlay stays detached. When two overlays reorder in one transaction, the demoted overlay's prop update (e.g. fillColor) is also lost.
The GMS draw order is already controlled by GMSPolygon.zIndex (set in updateProps), so a reorder should not need to detach/re-attach the overlay at all.
Suggested fix
Make a reorder a no-op for the GMS attachment: defer the detach by one runloop tick and cancel it if the same view is re-mounted in the same (synchronous) transaction. Only a genuine removal (no matching re-mount) detaches:
// RNMapsGooglePolygonView.mm (add BOOL _detachScheduled;)
- (void)didInsertInMap:(AIRGoogleMap *)map {
_detachScheduled = NO; // cancel a pending detach → this is a reorder, not a removal
_pendingMap = map;
[self attachIfReady];
}
- (void)didRemoveFromMap {
_detachScheduled = YES;
__weak RNMapsGooglePolygonView *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
RNMapsGooglePolygonView *s = weakSelf;
if (s == nil || !s->_detachScheduled) return; // re-mounted → keep attached
s->_detachScheduled = NO;
s->_view.map = nil;
});
}(prepareForRecycle still does the real teardown for genuine removals and resets the flag.)
A working patch-package patch for 1.27.2 is available; PR can follow.
Source: react-native-maps/react-native-maps