最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React Native Maps Callout Rendering - Stack Overflow

programmeradmin2浏览0评论

I am using react-native-maps to display markers for train stations in my area. Each marker has a Callout with real time data of approaching trains.

The issue is; every callout is being rendered in the background for every marker I have on the map. Also each callout is being re-rendered as I have new data from real time API. This is causing hundreds of views being rendered even though I only need the callout of the marker that is pressed.app screenshot

Is there a way to make sure no callout is being rendered until user presses on a specific marker? After the press; I also want to make sure only that specific marker's callout is being rendered and displayed.

My code:

MapScreen:

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

Markers ponent:

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 ponent:

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

I am using react-native-maps to display markers for train stations in my area. Each marker has a Callout with real time data of approaching trains.

The issue is; every callout is being rendered in the background for every marker I have on the map. Also each callout is being re-rendered as I have new data from real time API. This is causing hundreds of views being rendered even though I only need the callout of the marker that is pressed.app screenshot

Is there a way to make sure no callout is being rendered until user presses on a specific marker? After the press; I also want to make sure only that specific marker's callout is being rendered and displayed.

My code:

MapScreen:

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

Markers ponent:

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 ponent:

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
Share Improve this question asked Jan 22, 2020 at 17:30 Onur EkerOnur Eker 9401 gold badge8 silver badges11 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 0

On ComponentDidMount, you should fetch data for all trains so that all markers can be set on their positions. you can do this using once('value') event of firebase this event only fetches data from a refernce once when it is called so you will call it on ponent did mount.

READ MORE ABOUT ONCE('VALUE')

Now as you have all the pointers on their positions, user can click any one of the pointer to Track It's movement right?

So each pointer must have something unique, like Train ID or something i dont know your database structure so i am assuming that you have train ID, Now in the onPress function of marker you should pass this TrainID.

example:

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

Now in TrackSpecificTrain function you should call your database refence with the train ID and firebase on('value') event, Now you will keep getting real time data of the selected train and you can update your local state stationData with the new data ing from firebase.

Example


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")

}

Now here we are also using RemoveTracker because you may need to stop tracking previous train if user clicks on another marker so it will start tracking the trainID on the new marker and stop tracking previous one!.

I actually found the answer myself. I have created a reference to each marker, then passed an onPress property to <MapView.Marker> and showCallout property to its Callout ponent.

Markers ponent:

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>
    );
  });
}

And Callout ponent only fetches data when showCallOut is true. In Callout ponent

useEffect(() => {
    if (props.showCallOut === true) {
      fetchTrainDepartures();

      const intervalId = setInterval(fetchTrainDepartures, 10000);
      return () => clearInterval(intervalId);
    }
  }, []);

So, unless you click on a marker, the local state stays at null and callouts doesn't fetch any data.

When you click on marker at index 0:

  • clickedMarkerRef is now 0.
  • showCallout is true => {clickMarkerRef === index}
  • on Callout.js file under useEffect hook => props.showCallout is true.
  • Data is fetched only for this callout.

Inside MapView use Marker, Callout like this. This callout view disappears when user clicks on other marker(then new markers view appears) or on map itself.

 <Marker key={i} coordinate={{latitude:latitude, longitude:longitude}}>
  <Callout tooltip={true} onPress={()=>this.processCalloutClick(item)}>
     <View style={{backgroundColor:'white',width:80, height:40, alignItems:'center'}}>
       <Text>{item.locationName}{"\n"}{"...More info"}</Text>
     </View>
  </Callout>
</Marker>

发布评论

评论列表(0)

  1. 暂无评论