【问题标题】:React Native Maps Callout RenderingReact Native Maps 标注渲染
【发布时间】:2026-01-23 16:45:02
【问题描述】:

我正在使用react-native-maps 显示我所在地区的火车站的标记。每个标记都有一个标注,其中包含接近火车的实时数据。

问题是;对于我在地图上的每个标记,每个标注都在背景中呈现。此外,每个标注都被重新渲染,因为我有来自实时 API 的新数据。即使我只需要按下的标记的标注,这也会导致数百个视图被渲染。app screenshot

有没有办法确保在用户按下特定标记之前不会呈现标注?新闻发布会后;我还想确保只渲染和显示特定标记的标注。

我的代码:

地图屏幕:

const MapScreen = props => {
  // get user location from Redux store
  // this is used to center the map
  const { latitude, longitude } = useSelector(state => state.location.coords)

  // The MapView and Markers are static
  // We only need to update Marker callouts after fetching data
  return(
    <SafeAreaView style={{flex: 1}}>
    <MapView
        style={{flex: 1}}
        initialRegion={{
          latitude:  parseFloat(latitude) || 37.792874,
          longitude: parseFloat(longitude) || -122.39703,
          latitudeDelta: 0.06,
          longitudeDelta: 0.06
        }}
        provider={"google"}
      >
        <Markers />
      </MapView>
      </SafeAreaView>
  )
}

export default MapScreen

标记组件:

const Markers = props => {
  const stationData = useSelector(state => state.stationData)

  return stationData.map((station, index) => {
    return (
      <MapView.Marker
        key={index}
        coordinate={{
          // receives station latitude and longitude from stationDetails.js
          latitude: parseFloat(stationDetails[station.abbr].gtfs_latitude),
          longitude: parseFloat(stationDetails[station.abbr].gtfs_longitude)
        }}
        image={stationLogo}
        zIndex={100}
        tracksInfoWindowChanges={true}
      >
        <MapView.Callout
          key={index}
          tooltip={true}
          style={{ backgroundColor: "#ffffff" }}
        >
          <View style={styles.calloutHeader}>
            <Text style={{ fontWeight: "bold" }}>{station.name}</Text>
          </View>
          <View style={styles.calloutContent}>
            <StationCallout key={index} station={stationData[index]} />
          </View>
        </MapView.Callout>
      </MapView.Marker>
    );
  });
};

StationCallout 组件:

const StationCallout = (props) => {
  return(
    props.station.etd.map((route, index) => {
      const approachingTrains = function() {
        trainText = `${route.destination} in`;

        route.estimate.map((train, index) => {
          if (index === 0) {
            if (train.minutes === "Leaving") {
              trainText += ` 0`;
            } else {
              trainText += ` ${train.minutes}`;
            }
          } else {
            if (train.minutes === "Leaving") {
              trainText += `, 0`;
            } else {
              trainText += `, ${train.minutes}`;
            }
          }
        });

        trainText += " mins";

        return <Text>{trainText}</Text>;
      };

      return <View key={index}>
      {approachingTrains()}
      </View>;
    })
  )
};

export default StationCallout

【问题讨论】:

    标签: javascript react-native google-maps react-native-maps


    【解决方案1】:

    在 ComponentDidMount 上,您应该获取所有列车的数据,以便可以在其位置上设置所有标记。您可以使用 firebase 的 once('value') 事件来执行此操作,此事件仅在调用时从引用中获取数据一次,因此您将在组件安装时调用它。

    READ MORE ABOUT ONCE('VALUE')

    现在你已经把所有的指针都放在了它们的位置上,用户可以点击任何一个指针来跟踪它的运动,对吗?

    所以每个指针必须有一些独特的东西,比如火车 ID 或我不知道你的数据库结构的东西,所以我假设你有火车 ID, 现在在标记的 onPress 函数中,您应该传递此 TrainID。

    示例:

    onPress={()=> this.TrackSpecificTrain(trainID)  }
    

    现在在 TrackSpecificTrain 函数中,您应该使用火车 ID 和 firebase on('value') 事件调用您的数据库引用,现在您将继续获取所选火车的实时数据,您可以使用来自 firebase 的新数据。

    例子

    
    TrackSpecificTrain=(trainID)=>{
    const ref = database().ref(`YourTrainsRef/${trainID}/`)
      ref.on('value',( snapshot)=>{
               //Update your local state with new data in snapshot
            })
    }
    
    
    RemoveTracker=(trainID)=>{
    const ref = database().ref(`YourTrainsRef/${trainID}/`)
    
    ref.off("value")
    
    }
    

    现在我们在这里也使用RemoveTracker,因为如果用户单击另一个标记,您可能需要停止跟踪上一列火车,以便它开始跟踪新标记上的 trainID 并停止跟踪前一列!。

    【讨论】:

      【解决方案2】:

      其实我自己找到了答案。我创建了对每个标记的引用,然后将 onPress 属性传递给 并将 showCallout 属性传递给它的 Callout 组件。

      标记组件:

      export default function Markers() {
        const {
          stations: { station }
        } = require("../../bartData/stations");
      
        const [clickedMarkerRef, setClickedMarkerRef] = useState(null)
      
        return station.map((trainStation, index) => {
          return (
            <MapView.Marker
              key={trainStation.abbr}
              coordinate={{
                latitude: parseFloat(trainStation.gtfs_latitude),
                longitude: parseFloat(trainStation.gtfs_longitude)
              }}
              image={Platform.OS === "ios" ? station_ios : station_android}
              zIndex={100}
              tracksInfoWindowChanges={true}
              onPress={() => setClickedMarkerRef(index)}
            >
              <CalloutContainer
                key={trainStation.abbr}
                stationName={trainStation.name}
                stationAbbr={trainStation.abbr}
                showCallOut={clickedMarkerRef === index}
              />
            </MapView.Marker>
          );
        });
      }
      

      而且 Callout 组件仅在 showCallOut 为 true 时才获取数据。 在标注组件中

      useEffect(() => {
          if (props.showCallOut === true) {
            fetchTrainDepartures();
      
            const intervalId = setInterval(fetchTrainDepartures, 10000);
            return () => clearInterval(intervalId);
          }
        }, []);
      

      因此,除非您单击标记,否则本地状态将保持为空,并且标注不会获取任何数据。

      当您点击索引 0 处的标记时:

      • clickedMarkerRef 现在为 0。
      • showCallout 为 true => {clickMarkerRef === index}
      • 在 useEffect 挂钩下的 Callout.js 文件上 => props.showCallout 为 true。
      • 仅为此标注提取数据。

      【讨论】: