Dynamically Adding Markers on google-map-react - javascript

What I wan't to do is to show the location picked from some mobile devices on the Map.
Data about the locations are there..
What I need here is to add Markers on the map depending on the data received from the server.
Assume I have set the location data ({Lat,Lang}) to the state markers
Then How can I add this to show in Map.
My Map Code is as follows!
import React, { Component } from 'react';
import GoogleMapReact from 'google-map-react';
const AnyReactComponent = ({ text }) => <div>{text}</div>;
class MyClass extends Component {
constructor(props){
super(props);
}
render() {
return (
<GoogleMapReact
defaultCenter={this.props.center}
defaultZoom={this.props.zoom}
style={{height: '300px'}}
>
<AnyReactComponent
lat={59.955413}
lng={30.337844}
text={'Google Map'}
/>
</GoogleMapReact>
);
}
}
MyClass.defaultProps = {
center: {lat: 59.95, lng: 30.33},
zoom: 11
};
export default MyClass;
This Code is from the answer Implementing google maps with react
Used npm package :- google-map-react

You may try:
import React, { Component } from 'react';
import GoogleMapReact from 'google-map-react';
const AnyReactComponent = ({ img_src }) => <div><img src={img_src} className="YOUR-CLASS-NAME" style={{}} /></div>;
class MyClass extends Component {
constructor(props){
super(props);
this.state = {
markers: [],
}
}
componentDidMount(){
// or you can set markers list somewhere else
// please also set your correct lat & lng
// you may only use 1 image for all markers, if then, remove the img_src attribute ^^
this.setState({
markers: [{lat: xxxx, lng: xxxx, img_src: 'YOUR-IMG-SRC'},{lat: xxxx, lng: xxxx, img_src: 'YOUR-IMG-SRC' },{lat: xxxx, lng: xxxx, img_src: 'YOUR-IMG-SRC'}],
});
}
render() {
return (
<GoogleMapReact
defaultCenter={this.props.center}
defaultZoom={this.props.zoom}
style={{height: '300px'}}
>
{this.state.markers.map((marker, i) =>{
return(
<AnyReactComponent
lat={marker.lat}
lng={marker.lng}
img_src={marker.img_src}
/>
)
})}
</GoogleMapReact>
);
}
}
MyClass.defaultProps = {
center: {lat: 59.95, lng: 30.33},
zoom: 11
};
If this has error, please show here too, then we can fix it later
===========
ADDED EXAMPLE FOR CLICK-EVENT ON MARKERS
markerClicked(marker) {
console.log("The marker that was clicked is", marker);
// you may do many things with the "marker" object, please see more on tutorial of the library's author:
// https://github.com/istarkov/google-map-react/blob/master/API.md#onchildclick-func
// Look at their examples and you may have some ideas, you can also have the hover effect on markers, but it's a bit more complicated I think
}
render() {
return (
<GoogleMapReact
defaultCenter={this.props.center}
defaultZoom={this.props.zoom}
style={{height: '300px'}}
>
{this.state.markers.map((marker, i) =>{
return(
<AnyReactComponent
lat={marker.lat}
lng={marker.lng}
img_src={marker.img_src}
onChildClick={this.markerClicked.bind(this, marker)}
/>
)
})}
</GoogleMapReact>
);
}
Once again, post here some errors if any ^^ !

Be careful. You said react-google-map but you are using google-map-react. Those are 2 different packages. Do not mix up their documentation.

Related

React Google Maps Api doesn't render children elements at first render

I'm trying to use Google Maps Api in my app, everything is fine until I want to display a Marker at the first render of the map. The Marker is not showing up, but if I add one more Marker after the render is done, the Marker will appear.
So the problem is that I want to render the map with a Marker already there, I don't want to wait for some location to be selected.
I want to receive lat and lng from props, but for now I've made an hard coded const (center).
import React, { useMemo } from "react";
import { useJsApiLoader, GoogleMap, Marker } from "#react-google-maps/api";
export default function GoogleMaps({ lat, lng }) {
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY,
});
const center = useMemo(() => ({ lat: 42.4332, lng: 20.4343 }), []);
if (!isLoaded) {
return <h2>Calculating Locations..</h2>;
}
return (
isLoaded && (
<GoogleMap
center={center}
zoom={17}
mapContainerStyle={{ width: "450px", height: "400px" }}
options={{ disableDefaultUI: true, mapId: "deleted for this snippet" }}
>
<Marker position={center} />
</GoogleMap>
)
);
}
Have you tried importing and using MarkerF instead of Marker?
See: https://github.com/JustFly1984/react-google-maps-api/issues/3048#issuecomment-1166410403
"MarkerF is functional component vs class based Marker component, which does not work with react Strict and/or react#17+"
Also, there are similar issues discussed here: Markers not rendering using #react-google-maps/api and here: Map Marker don't show up (Marker rendered before Map)- ReactJS with #react-google-maps/api

Keep updating markers on map on top of an overlay image - google maps react

I have the following code which has an overlay image on the map.
However, i would like to display markers on top of this overlay image. The markers come from a users array which is constantly being updated. I want to loop through these markers and place them on the map, but keep updating them if they change longitude/latitude.
I have tried some code below, the data is getting passed to the components correctly, but the markers are always staying in the same position. Could anyone tell me what i'm doing wrong?
Below is my main map code
return (
<div className='map'>
<div className='google-map'>
<GoogleMapReact
bootstrapURLKeys={{ key: 'KEYID' }}
defaultCenter={location}
defaultZoom={zoomLevel}>
{users?.map((user, i) => {
return (
<MyGreatPlace
key={i}
latitude={user.latitude}
longitude={user.longitude}
sender={user.sender}
/>
);
})}
<OverlayImage lat={location.lat} lng={location.lng} text={'A'} />
</GoogleMapReact>
</div>
</div>
);
Below is MyGreatPlace.jsx file for the markers:
export default function MyGreatPlace({ i, latitude, longitude, sender }) {
console.log(sender);
return (
<div style={{ width: 60, height: 60, background: 'green' }}>
{latitude} {longitude} {sender}
</div>
);
}
Below is OverlayImage.jsx file for the overlay image:
export default class OverlayImage extends Component {
static propTypes = {
text: PropTypes.string,
};
static defaultProps = {};
shouldComponentUpdate = shouldPureComponentUpdate;
render() {
return <div>{<img src={floorplan} alt='floorplan' />}</div>;
}
}
Do i need to make the MyGreatPlace component the same as the overlay component? I'm not sure how props would work getting the longitude/latitude passed in?
The solution was to change MyGreatPlace file to the same as the overlay component, to look like this:
export default class MyGreatPlace extends Component {
static propTypes = {
text: PropTypes.string,
};
static defaultProps = {};
shouldComponentUpdate = shouldPureComponentUpdate;
render() {
return <div>{this.prop.text}</div>;
}
}

google-maps-react centers me in the middle of the ocean when given new bounds

I am using the google-maps-react package and I am trying to have bounds for my map. They should change based on values passed to the component through props. It does successfully do this, but after the first load of my website instead of reloading the map so that I can see all the markers, it just puts me at lat: 0.0 and long: 0.0. If I zoom out I can see all of my markers, but the map does not resize itself.
It does successful load! Just doesn't load the bounds. See the images below.
After I save my file and it reloads
After I leave the page and come back, or refresh
Here is what I've tried:
import { Map, Marker, GoogleApiWrapper } from "google-maps-react";
class MapExample extends Component {
constructor(props) {
super(props);
this.state = {
bounds: null
};
this.handleMapLoad = this.handleMapLoad.bind(this);
}
handleMapLoad() {
const bounds = new this.props.google.maps.LatLngBounds();
for (let loc of this.props.originsCoords) bounds.extend({ lat: loc.lat, lng: loc.lng });
this.setState({bounds:bounds});
}
render() {
return (
<Map
onReady={this.handleMapLoad}
google={this.props.google}
bounds={this.state.bounds}
style={{width:'50%', height:'50%'}}
>
{this.props.originsCoords.map((loc, i) => (
<Marker key={i} position={{ lat: loc.lat, lng: loc.lng }} />
))}
</Map>
);
}
}
export default GoogleApiWrapper({
apiKey: "api-key"
})(MapExample);

Set marker timeout in React

The animation prop in this Marker component rendering gets a marker to bounce if there's one and drop if there are multiple, using the marker's array:
<Marker
key={index}
position={{lat: marker.lat, lng: marker.lng}}
onClick={() => props.handleMarkerClick(marker)}
animation={array.length === 1 ? google.maps.Animation.BOUNCE : google.maps.Animation.DROP}
>
However, my goal is to set a 750ms timeout for the bounce (based on an answer to another SO question). I can't do this with the conditional ternary operator, so I made two attempts at creating a function for this, replacing the animation property in the Marker component with animation={array.length === 1 ? bounceMarker() : google.maps.Animation.DROP} and both had issues.
This one I thought would work, but using google.maps.Animation.BOUNCE; throws an ESLint error "Expected an assignment or function call and instead saw an expression."
const bounceMarker = () => {
google.maps.Animation.BOUNCE;
setTimeout(() => {
marker.setAnimation(null);
}, 750);
};
This function is based on Google's markers tutorial, and caused the error "marker.getAnimation is not a function":
const bounceMarker = () => {
if (marker.getAnimation() !== null) {
marker.setAnimation(null);
} else {
marker.setAnimation(google.maps.Animation.BOUNCE);
setTimeout(() => {
marker.setAnimation(null);
}, 750);
}
};
In case it's useful here's my full code from this file, with the two functions commented out:
// Specifies global variable to ESLint (bundled with create-react-app), circumventing no-undef rule. See https://eslint.org/docs/user-guide/configuring#specifying-globals
/* global google */
// This component's code is from react-google-maps implementation instructions https://tomchentw.github.io/react-google-maps/#installation
import React, { Component , Fragment } from 'react';
import { withScriptjs , withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps';
const MyMapComponent = withScriptjs(
withGoogleMap(props => (
<GoogleMap
zoom={props.zoom}
defaultCenter={{ lat: 47.6093, lng: -122.3309 }}
>
{/* If there are markers, filters all visible markers (creating new array) then maps over newly created array taking the marker and marker's array index as arguments, rendering each Marker component with the marker index set as the key and the marker's lat and long as the position */}
{props.markers &&
props.markers.filter(marker => marker.isVisible)
// "Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity ... When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort" https://reactjs.org/docs/lists-and-keys.html
.map((marker, index, array) => {
// const bounceMarker = () => {
// if (marker.getAnimation() !== null) {
// marker.setAnimation(null);
// } else {
// marker.setAnimation(google.maps.Animation.BOUNCE);
// setTimeout(() => {
// marker.setAnimation(null);
// }, 750);
// }
// };
// const bounceMarker = () => {
// google.maps.Animation.BOUNCE;
// setTimeout(() => {
// marker.setAnimation(null);
// }, 750);
// };
const venueInfo = props.venues.find(venue => venue.id === marker.id);
return (
<Marker
key={index}
position={{lat: marker.lat, lng: marker.lng}}
// Marker click event listener, defined in App component class
onClick={() => props.handleMarkerClick(marker)}
animation={array.length === 1 ? bounceMarker() : google.maps.Animation.DROP}
>
{/* Show marker's InfoWindow when its isOpen state is set to true (set in app.js) */}
{marker.isOpen &&
<InfoWindow>
{/* If a venueInfo is not falsey and: 1) if there's a name and bestPhoto property, return the venue photo and name; 2) if there's only a name property, display the name only; 3) if there's only a photo property, display the photo only. If neither are available and/or venueInfo is falsy display text indicating no info available. See SO question about multiple ternary operators https://stackoverflow.com/questions/7757549/multiple-ternary-operators */}
{venueInfo && venueInfo.name && venueInfo.bestPhoto ?
<Fragment>
<p>{venueInfo.name}</p>
<img src={`${venueInfo.bestPhoto.prefix}200x200${venueInfo.bestPhoto.suffix}`}
// Screen readers already announce as image; don't need the word "image", "photo", etc.
alt={"Venue"}
/>
</Fragment> : venueInfo && venueInfo.name ? <p>{venueInfo.name}</p> : venueInfo && venueInfo.bestPhoto ? <img src={`${venueInfo.bestPhoto.prefix}200x200${venueInfo.bestPhoto.suffix}`}
// Screen readers already announce as image; don't need the word "image", "photo", etc.
alt={"Venue"}
/> : <p>No info available</p>}
</InfoWindow>
}
</Marker>
);
})}
</GoogleMap>
))
);
export default class Map extends Component {
render() {
return (
<MyMapComponent
// This is making the this.setState passed into Map component (as its prop) inside App's component class's render method available to MyMapComponent, which is how props from this.setState are eventually included inside MyMapComponent class (such as zoom={props.zoom})
{...this.props}
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?v=3.exp&key=AIzaSyDjl8LxY7Edfulq6t_VDaQsYY4ymPjwN0w"
// CSS declarations are placed in double curly braces because attributes accept JS objects; this is how to include an object literal. See https://stackoverflow.com/questions/22671582/what-is-the-purpose-of-double-curly-braces-in-reacts-jsx-syntax
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%`, width: `75%` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
);
}
}
You are getting this error
marker.getAnimation is not a function
since marker is not the actual google.maps.Marker object.
Instead of rendering markers and afterwards updating Marker.animation property, how about to render the marker with a timeout (basically the same way as it is demonstrated in Marker Animations With setTimeout() example)?
For that purpose it is proposed to introduce a state for keeping animated markers
class MarkersAnimation extends React.Component {
constructor(props) {
super(props);
this.state = {
markers: []
};
}
render() {
return (
<GoogleMap
defaultZoom={this.props.zoom}
defaultCenter={this.props.center}
>
{this.state.markers.map((place, i) => {
return (
<Marker
animation={google.maps.Animation.BOUNCE}
id={place.id}
key={place.id}
position={{ lat: place.lat, lng: place.lng }}
/>
);
})}
</GoogleMap>
);
}
componentDidMount() {
const { markers, delay } = this.props;
this.interval = setInterval(() => {
this.setState(prev => ({
markers: prev.markers.concat([markers.shift()])
}));
if (!markers.length) clearInterval(this.interval);
}, delay);
}
componentWillUnmount() {
clearInterval(this.interval);
}
}
Here is a demo

React Google Maps InfoWindow toggle display one at a time

I am currently using react-google-maps and I am able to display the marker and InfoWindow correctly by clicking on the marker (from the map or from the list). But once I click on a marker to open, the previous InfoWindow stays open even when I select a new one.
I am able to close the InfoWindow if I click on the 'x' or marker from list. But I'm not sure how to close other InfoWindows automatically and only open the current marker's InfoWindow.
I have looked at several similar questions to get an idea: When displaying multiple markers on a map, how to open just one info window, when clicking on a marker?, React-Google-Map multiple Info window open
.
But I'm still having trouble with passing and checking for unique marker so that only the InfoWindow which matches the marker ID is displayed.
class MarkerInfoList extends Component {
constructor(props){
super(props);
this.state = {
isOpen: false
};
}
handleToggleOpen = (markerId) => {
this.setState({
isOpen: true
});
}
handleToggleClose = (markerId) => {
this.setState({
isOpen: false
});
}
render() {
const isOpen = this.state.isOpen;
return (
<div>
{this.state.isOpen ? (
<ul>
<li onClick={() => this.handleToggleClose()}>{this.props.venue}</li>
<Marker
key={this.props.index}
id={this.props.index}
position={{ lat: this.props.lat, lng: this.props.lng}}
defaultAnimation={google.maps.Animation.DROP}
onClick={() => this.handleToggleOpen(this.props.index)}>
<InfoWindow
id = {this.props.index}
onCloseClick={() => this.setState({isOpen: false})}>
<div key={this.props.index}>
<h4>{this.props.venue}</h4>
</div>
</InfoWindow>
</Marker>
</ul>
) : (
<ul>
<li onClick={() => this.handleToggleOpen()}>{this.props.venue}</li>
</ul>
)
}
</div>
)
}
}
export default MarkerInfoList;
I wanted to toggle individual windows but also needed to know if the locations were "active" on a parent component. So in the parent component I have state the with an active key. I am passing that down to the Map as activeKey component as well as the initial list with all the location data as locations.
<GoogleMap defaultZoom={16} defaultCenter={this.state.currentLocation}>
{this.props.locations.map((location, i) => (
<Marker
key={location.key}
position={{
lat: location.latitude,
lng: location.longitude
}}
onClick={() => {
this.props.toggleLocationsActive(location.key);
// this.setCurrentLocation(location.latitude, location.longitude);
}}
>
{location.key === this.props.activeKey && (
<InfoWindow onCloseClick={props.onToggleOpen}>
<div>
{location.orgName}
{location.orgName !== location.programName &&
"/" + location.programName}
</div>
</InfoWindow>
)}
</Marker>
))}
</GoogleMap>
In the parent component I had state = {activeKey: ""} and the following method:
toggleLocationsActive = locationKey => {
this.setState({
activeKey: locationKey
});
};
Both are are passed down in props:
<Map
activeKey={this.state.activeKey}
toggleLocationsActive={this.toggleLocationsActive}
/>
Please note that I if you don't require this state to exist in the parent component you could do this within the map component.

Categories

Resources