maps
maps copied to clipboard
PointAnnotation in wrong position when added to MapView after MapView's first render
Describe the bug
On iOS (everything works as intended on Android), when a PointAnnotation is added as a child of a MapView, without prompting a re-mount of the MapView, the PointAnnotation is displayed in the top left corner of the device and not at the coordinate specified in the PointAnnotation's props. This sequence of events is occurring in the following use case: app opens, begins loading content, map renders, content loads, PointAnnotations are created based on loaded content.
The PointAnnotations are not first direct children of the MapView. There is a child of the MapView which itself has children which are PointAnnotations.
We are rendering CircleLayers based on a Source along side the PointAnnotations. We do not want the Map to re-mount (along with its CircleLayer) as the PointAnnotations are added/deleted/changed often, and it would be inefficient to re-mount the map each time.
To Reproduce Though in our working code the Map's lifecycle is managed by the lifecycle methods, the same effect can be created using the below code and Metro. If you run the below code on a device, leaving the 6th entry of the 'coordinates' array commented, then uncomment the 6th line, and write the file (triggering a reload from Metro) the MapView will not re-mount, but the components will re-render. The PointAnnotation created from the 6th entry will display in the top left corner of the screen.
Example:
import React from 'react'
import { View } from 'react-native'
import {
MapView,
ShapeSource,
LineLayer,
PointAnnotation,
Camera,
} from '@rnmapbox/maps'
class App extends React.Component {
render () {
const coordinates = [
[ Math.random() * 360 - 180, Math.random() * 180 - 90 ],
[ Math.random() * 360 - 180, Math.random() * 180 - 90 ],
[ Math.random() * 360 - 180, Math.random() * 180 - 90 ],
[ Math.random() * 360 - 180, Math.random() * 180 - 90 ],
[ Math.random() * 360 - 180, Math.random() * 180 - 90 ]
//[ Math.random() * 360 - 180, Math.random() * 180 - 90 ]
]
return (
<MapView>
<ChildA />
<ChildB
annotations={coordinates}
/>
</MapView>
)
}
}
class ChildB extends React.Component {
render () {
const { annotations } = this.props
return (
<View>
{annotations.map((annotation) => (
<PointAnnotation
id={`pa-${annotation[0]}-${annotation[1]}`}
key={`pa-${annotation[0]}-${annotation[1]}`}
coordinate={annotation}
/>
))}
</View>
)
}
}
class ChildA extends React.Component {
render () {
return (
<ShapeSource id="circleLayer">
<CircleLayer id="circleLayer" />
</ShapeSource>
)
}
}
Expected behavior
The PointAnnotations should be displaying at the specified coordinates.
Actual behavior
The PointAnnotations which are added to the MapView after its mounting are displayed in the top left corner of the screen.
Screenshots
If applicable, add screenshots to help explain your problem.
Versions
- Platform: iOS
- Platform OS: iOS 15.5
- Device: iPhone 13
- Emulator/ Simulator: no
- Dev OS: OSX 12.2.1 Monterey
- @rnmapbox/maps Version 8.5.0
- Mapbox GL
- React Native Version 0.66.4
Additional context
I have dug into the underlying iOS code and found that the newly added PointAnnotation is never added to the MapView's (RCTMGLMapView.m) _pointAnnotations member. In pseudo code (though with accurate member function names), the process by which a PointAnnotation is added to the map seems to be the following:
PointAnnotation->setMap
MapView->addToMap (called within the above setMap, adds the PA to _pointAnnotations)
MapView->insertReactSubview
RCTSetChildren
NONE of the above functions are called when a PointAnnotation is added to the MapView after the MapView's initial rendering.
+1 and when you move and zoom out the map it's going to move like crazy
@lockieluke I have not experienced this
+1 experiencing the same issue with PointAnnotations appearing in the top left
@bwiitttyyyyy thanks for the report, unfortunately we're not actively looking into legacy mapbox (< v10) issues. Pr's always wellcome.
@mfazekas This report sounds very much like the behavior from https://github.com/rnmapbox/maps/issues/2115, so I don't think it's safe to assume this is only a pre v10 issue.
@mfazekas Was there a change that closed this, or was it closed as a duplicate of #2115? The reason I called attention to this issue, is that it appears to confirm that #2115 should be reopened.
@michaek I could not reproduce using this modified example bellow on v10, but not sure as no annotation appeared at all for me. If the issue still there please report with a complete example like bellow.
import React from 'react';
import { View } from 'react-native';
import {
MapView,
ShapeSource,
LineLayer,
CircleLayer,
PointAnnotation,
Camera,
} from '@rnmapbox/maps';
const features = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
id: 'a-feature',
properties: {
icon: 'example',
text: 'example-icon-and-label',
},
geometry: {
type: 'Point',
coordinates: [-74.00597, 40.71427],
},
},
{
type: 'Feature',
id: 'b-feature',
properties: {
text: 'just-label',
},
geometry: {
type: 'Point',
coordinates: [-74.001097, 40.71527],
},
},
{
type: 'Feature',
id: 'c-feature',
properties: {
icon: 'example',
},
geometry: {
type: 'Point',
coordinates: [-74.00697, 40.72427],
},
},
],
};
class App extends React.Component {
render() {
const coordinates = [
[Math.random() * 360 - 180, Math.random() * 180 - 90],
[Math.random() * 360 - 180, Math.random() * 180 - 90],
[Math.random() * 360 - 180, Math.random() * 180 - 90],
[Math.random() * 360 - 180, Math.random() * 180 - 90],
[Math.random() * 360 - 180, Math.random() * 180 - 90],
[Math.random() * 360 - 180, Math.random() * 180 - 90]
];
// <Camera centerCoordinate={[-74.00597, 40.71427]} zoomLevel={14} />
return (
<MapView style={{ flex: 1 }}>
<ChildA />
<ChildB annotations={coordinates} />
</MapView>
);
}
}
class ChildB extends React.Component {
render() {
const { annotations } = this.props;
return (
<View>
{annotations.map((annotation) => (
<PointAnnotation
id={`pa-${annotation[0]}-${annotation[1]}`}
key={`pa-${annotation[0]}-${annotation[1]}`}
coordinate={annotation}
/>
))}
</View>
);
}
}
class ChildA extends React.Component {
render() {
return (
<ShapeSource id="circleLayer" shape={features}>
<CircleLayer
id="circleLayer"
style={{ circleColor: '#ff0000', circleRadius: 10.0 }}
/>
</ShapeSource>
);
}
}
export default App;
That makes sense, @mfazekas. My interest is in getting #2115 reopened, since its example still reproduces the issue.
@michaek I could not reproduce #2115 anymore. Tested on iOS. Which version have you tested?!

Were you testing the original report template, or the updated template to show that the same issue was present when state caused the view to re-render? https://github.com/rnmapbox/maps/issues/2115#issuecomment-1216343341 I have tested and reproduced this in iOS (15.6.1), in both simulator and physical device.
@michaek Both worked me just fine. What is your rnmapbox/maps version? Pls make sure that both #2205 and #2203 present in your version.
cat io/Podfile.lock | grep rnmapbox
- rnmapbox-maps (10.0.0-beta.38):
@mfazekas I don't think I'll be able to test this one soon, but since my testing was prior to #2203 and you're not able to reproduce, I am happy to assume those changes addressed the underlying issue. Thanks for pointing out the relevant changes!
In Mapbox version 10 on ios, PointAnnotations were also not working for me.
I was fetching a list of coordinates async and mapping them onto the mapview directly in the return statement of my component.
The only way I could get it working on ios is the below example. In addition, without the custom View
in the PointAnnotation
children, it doesn't render anything at all.
Hope this helps someone else.
const renderAnnotations = useCallback(() => {
return courts.map((court) => {
return (
<PointAnnotation
key={court.id}
id={court.id}
coordinate={[court.lng, court.lat]}>
<View style={{
height: 30,
width: 30,
backgroundColor: '#00cccc',
borderRadius: 50,
borderColor: '#fff',
borderWidth: 3
}} />
<Callout title={court.parkName} />
</PointAnnotation>
);
});
},[courts]);
return (
<View style={styles.container}>
<ActivityIndicator style={styles.loading} color='white' />
<MapView
style={{ flex: 1 }}
styleURL={StyleURL.Dark}
attributionEnabled={false}>
{renderAnnotations()}
<Camera
centerCoordinate={//@ts-ignore
userHomeCourtCoords.values}
zoomLevel={12}
followZoomLevel={14}
/>
<UserLocation
animated={true}
showsUserHeadingIndicator={true}
/>
</MapView>
</View>
);
};