KevinKipkemei
KevinKipkemei

Reputation: 43

Updating markers without reloading the whole map

import React from 'react';
import {GoogleMap, withScriptjs, withGoogleMap, Marker} from 'react-google-maps';
import {db} from './Firebase';
import {useState, useEffect}  from 'react';
import InfoWindow from 'react-google-maps/lib/components/InfoWindow';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import { CssBaseline } from '@material-ui/core';
import { makeStyles } from '@material-ui/styles';
import IconButton from '@material-ui/core/IconButton'
import KeyboardArrowLeftRounded from '@material-ui/icons/KeyboardArrowLeftOutlined';


const useStyles = makeStyles( theme =>({
  appBar: {
    backgroundColor: '#1976d2'
  },
  card :{
    marginTop: 60
  }
}));

const Maps = (props) =>
  {
    const classes = useStyles();
    const [positions, setPosition] = useState([])
    useEffect(() => {
        const unsub = db.collection('Location').onSnapshot (snapshot => {
          const allPositions = snapshot.docs.map(doc => ({
            id: doc.id,
            ... doc.data()
          }));
          setPosition(allPositions);
        })
        return () => {
          unsub();
        };
      }, [])

    const WrappedMap = withScriptjs(withGoogleMap(props => (
      <GoogleMap defaultZoom = {13}
        defaultCenter = {{ lat: -1.292066 , lng : 36.821945}}>
        {
          positions.map(positioning => (
              props.isMarkerShown &&
                <Marker key = {positioning.id}
                  position = {{lat: positioning.Latitude , lng: positioning.Longitude}}
                ></Marker>
            )
          )
        }

        {
          positions.map(positioning => (
            <InfoWindow key = {positioning.id} defaultPosition = {{lat: positioning.Latitude, lng: positioning.Longitude}}>
              <div>
                {positioning.Team} <br/>
                <a href = "/Nav"> Message </a>
              </div>
            </InfoWindow>
          ))
        }         

      </GoogleMap>)));

    return (
      <div>
        <div>
          <CssBaseline/>
          <AppBar position = "fixed" color = "primary" className = {classes.appBar}>
            <Toolbar>
              <IconButton color = "inherit" edge = "start" onClick={() => props.history.goBack()}>
                <KeyboardArrowLeftRounded/>
              </IconButton>
            </Toolbar>
          </AppBar>
        </div>
        <div>
          <Card className = {classes.card}>
            <CardContent>
              <div style = {{width: "97vw", height: "90vh"}}> 
                <WrappedMap
                  isMarkerShown 
                  googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=AIzaSyD29SDFXKcqARovEjwUqKl0ysEFKK7GCmU`}
                  loadingElement={<div style={{ height: `100%` }} />}
                  containerElement={<div style={{ height: `100%` }} />}
                  mapElement={<div style={{ height: `100%` }} />}
                  />
              </div>
            </CardContent>
          </Card>
        </div>
      </div>
    )
  };

export default Maps;

I am using the code above to render markers and infowindows for locations that I'm fetching from Firebase. Currently when an update is made in Firebase, the whole mapview is rendered again in order to update the position of the markers and infowindows. How do I go about re-rendering just the marker when an update is made in Firebase.

Upvotes: 1

Views: 2866

Answers (2)

Vadim Gremyachev
Vadim Gremyachev

Reputation: 59388

In your example WrappedMap gets re-created every time. One solution (to prevent Google Maps API reload and maps re-render) would be to place the instantiation of WrappedMap component outside of Maps:

const WrappedMap = withScriptjs(
  withGoogleMap(props => (
    <GoogleMap
      defaultZoom={5}
      defaultCenter={{ lat: -24.9929159, lng: 115.2297986 }}
    >
      {props.places.map(
        position =>
          props.isMarkerShown && (
            <Marker
              key={position.id}
              position={{
                lat: position.lat,
                lng: position.lng
              }}
            ></Marker>
          )
      )}
    </GoogleMap>
  ))
);

and then just pass positions prop to reflect updated positions on map:

function Map() {     
  return (
    <div>
      <WrappedMap
        isMarkerShown
        positions={positions}
        googleMapURL={`https://maps.googleapis.com/maps/api/js?key=AIzaSyD29SDFXKcqARovEjwUqKl0ysEFKK7GCmU`}
        loadingElement={<div style={{ height: `100%` }} />}
        containerElement={<div style={{ height: `400px` }} />}
        mapElement={<div style={{ height: `100%` }} />}
      />
    </div>
  );
}

Upvotes: 2

Ayushya
Ayushya

Reputation: 1920

You can prevent re-rendering by making use of PureComponent. In your case positioning is re-rendering the Marker and InfoWindow.

The Updated Code below will only re-render the updated/newly-added Markers.

import React from 'react';
import {GoogleMap, withScriptjs, withGoogleMap, Marker} from 'react-google-maps';
import {db} from './Firebase';
import {useState, useEffect}  from 'react';
import InfoWindow from 'react-google-maps/lib/components/InfoWindow';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import { CssBaseline } from '@material-ui/core';
import { makeStyles } from '@material-ui/styles';
import IconButton from '@material-ui/core/IconButton'
import KeyboardArrowLeftRounded from '@material-ui/icons/KeyboardArrowLeftOutlined';


const useStyles = makeStyles( theme =>({
  appBar: {
    backgroundColor: '#1976d2'
  },
  card :{
    marginTop: 60
  }
}));

class MarkerPureComponent extends React.PureComponent {
  render () {
    return (
      <Marker
        position = {this.props.position}
      >
      </Marker>
    )
  }
}

class InfoWindowPureComponent extends React.PureComponent {
  render () {
    return (
      <InfoWindow defaultPosition = {this.props.defaultPosition}>
        {this.props.children}
      </InfoWindow>
    )
  }
}

const Maps = (props) =>
  {
    const classes = useStyles();
    const [positions, setPosition] = useState([])
    useEffect(() => {
        const unsub = db.collection('Location').onSnapshot (snapshot => {
          const allPositions = snapshot.docs.map(doc => ({
            id: doc.id,
            ... doc.data()
          }));
          setPosition(allPositions);
        })
        return () => {
          unsub();
        };
      }, [])

    const WrappedMap = withScriptjs(withGoogleMap(props => (
      <GoogleMap defaultZoom = {13}
        defaultCenter = {{ lat: -1.292066 , lng : 36.821945}}>
        {
          positions.map(positioning => (
              props.isMarkerShown &&
                <MarkerPureComponent key = {positioning.id}
                  position = {{lat: positioning.Latitude , lng: positioning.Longitude}}
                ></MarkerPureComponent>
            )
          )
        }

        {
          positions.map(positioning => (
            <InfoWindowPureComponent key = {positioning.id} defaultPosition = {{lat: positioning.Latitude, lng: positioning.Longitude}}>
              <div>
                {positioning.Team} <br/>
                <a href = "/Nav"> Message </a>
              </div>
            </InfoWindowPureComponent>
          ))
        }         

      </GoogleMap>)));

    return (
      <div>
        <div>
          <CssBaseline/>
          <AppBar position = "fixed" color = "primary" className = {classes.appBar}>
            <Toolbar>
              <IconButton color = "inherit" edge = "start" onClick={() => props.history.goBack()}>
                <KeyboardArrowLeftRounded/>
              </IconButton>
            </Toolbar>
          </AppBar>
        </div>
        <div>
          <Card className = {classes.card}>
            <CardContent>
              <div style = {{width: "97vw", height: "90vh"}}> 
                <WrappedMap
                  isMarkerShown 
                  googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=AIzaSyD29SDFXKcqARovEjwUqKl0ysEFKK7GCmU`}
                  loadingElement={<div style={{ height: `100%` }} />}
                  containerElement={<div style={{ height: `100%` }} />}
                  mapElement={<div style={{ height: `100%` }} />}
                  />
              </div>
            </CardContent>
          </Card>
        </div>
      </div>
    )
  };

export default Maps;

Upvotes: 0

Related Questions