Search code examples
javascriptreact-nativeexporeact-native-maps

React Native Maps pressing Marker shows incorrect Callout and Marker


I'm using Expo 39.0.2 and react-native-maps 0.27.1 (latest version at the time of writing). I use Markers and Callouts to show points and their information on the map. However, whenever I click a Marker, another Callout show up and the original Marker is also replaced, but when I close the Callout, the Marker is back. This appears both on iOS and Android.

(The code is heavily simplified, so some functions might be missing)

import React, { useState, useEffect, useRef } from 'react';
import { View, Dimensions } from 'react-native';
import MapView, { UrlTile, Marker, Callout } from 'react-native-maps'
import { Text } from 'native-base';
import config from '../config.json';

function MapScreen({ navigation }) {
    const width = Dimensions.get('window').width;
    const height = Dimensions.get('window').height;

    const INITIALREGION = {
        latitude: 23.56621,
        longitude: 120.96666,
        latitudeDelta: 3,
        longitudeDelta: 3,
    }

    const MAP_TYPE = Platform.OS == 'android' ? 'none' : 'standard';
    const urlTemplate = 'http://c.tile.openstreetmap.org/{z}/{x}/{y}.png';

    const [stations_info, set_stations_info] = useState([]);

    useEffect(() => {
        fetch(config.request.hostname + config.request.api_prefix + "/station", {
            method: "GET"
        })
        .then((response) => response.json())
        .then((data) => {
            data = data.map((value) => {
                value.name = value.device;
                value.show = true;
                value.color = function_that_determines_color(value.record);
                return value;
            });
            set_stations_info(data);
        })
        .catch((error) => {
            console.error(error);
        });
    }, []);
    const mapRef = useRef();
    return (
        <View style={{ flex: 1 }}>
            <MapView
                style={{ flex: 1, width: width, height: height }}
                mapType={MAP_TYPE}
                initialRegion={INITIALREGION}
                ref={mapRef}
                >
                <UrlTile urlTemplate={urlTemplate} zIndex={1} />
                {
                    stations_info.filter(v => v.show).map((station, index) => (
                        <Marker
                            coordinate={{ latitude: station.latitude, longitude: station.longitude }}
                            title={station.name}
                            pinColor={station.color}
                            tracksViewChanges={true}
                            key={index}
                        >
                            <Callout onPress={() => navigation.navigate('Info', { station_id: station.id, name: station.name }) }>
                                <Text>Station: {station.name}</Text>
                                {
                                    Object.entries(station.record).map((value, key) => (
                                        <Text key={key}>{value[0]}: {value[1]}</Text>
                                    ))
                                }
                                <Text>Click to see more</Text>
                            </Callout> 
                        </Marker >
                    ))
                }
            </MapView>
        </View>
    );
}

enter image description here

Take the above GIF for example, the marker is red, but when clicking on the marker, it turns yellow, and the callout isn't showing the correct info (this callout should belong to another marker), and after closing it, the color of the marker is correct again.


Solution

  • I think I've figured out what happened.

    The problem is that, when multiple markers share the exactly same longitude and latitude, there's no guarantee that pressing on the marker(s) only triggers the topmost marker's onPress, instead multiple events may be fired. The workaround is that, either make sure that every marker has a distinct location, or add a small offset to the overlapping markers.