ReactJS: Google Map Component
Steps to implement google maps in Facebook’s ReactJS Web Application
1. Add npm module in your project for react-google-maps
npm install --save react-google-maps # or
yarn add react-google-maps
Reference : https://github.com/tomchentw/react-google-maps
Before this you have to include google maps api sdk in your index.ejs file
<script src=”https://maps.googleapis.com/maps/api/js?v=3.27&libraries=places,geometry&key=<YOUR_API_KEY>”></script>
How to get Google Maps Api Key ?
Follow the steps given in this link
2. Create Google Map Handler Component
'use strict';
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import update from 'react-addons-update';
import canUseDOM from 'can-use-dom';
import raf from 'raf';
import GoogleMapComponent from './gMapComponent';
const geolocation = (
canUseDOM && navigator.geolocation ?
navigator.geolocation :
({
getCurrentPosition(success, failure) {
failure('Your browser doesnt support geolocation.');
},
})
);
class GoogleMapHandlerComponent extends React.Component {
state = {
bounds: null,
center: null,
content: null,
radius: 400,
markers: []
};
constructor(props) {
super(props);
if (props !== null &&
props !== undefined &&
props.location !== null &&
props.location !== undefined &&
props.location.markers !== null &&
props.location.markers !== undefined) {
const locationInfo = {
center: props.location.center,
markers: props.location.markers
};
this.state = locationInfo;
}
}
componentDidMount() {
const tick = () => {
if (this.isUnmounted) {
return;
}
this.setState({ radius: Math.max(this.state.radius - 5, 0) });
if (this.state.radius > 10) {
raf(tick);
}
};
geolocation.getCurrentPosition((position) => {
if (this.isUnmounted) {
return;
}
this.setState({
center: {
lat: position.coords.latitude,
lng: position.coords.longitude,
},
content: 'Location found using HTML5.',
});
raf(tick);
}, (reason) => {
if (this.isUnmounted) {
return;
}
this.setState({
center: {
lat: 60,
lng: 105,
},
content: 'Error: The Geolocation service failed (${reason}).',
});
});
if (document.getElementsByClassName('pac-container').length > 0) {
const gmapSearchResultsBox = document.getElementsByClassName('pac-container')[0];
gmapSearchResultsBox.style.zIndex = '2000';
}
}
componentWillReceiveProps(nextProps) {
if (nextProps !== null &&
nextProps !== undefined &&
nextProps.location !== null &&
nextProps.location !== undefined &&
nextProps.location.markers !== null &&
nextProps.location.markers !== undefined) {
this.setState({
center: nextProps.location.center,
markers: nextProps.location.markers
});
}
}
componentDidUpdate() {
if (document.getElementsByClassName('pac-container').length > 0) {
const gmapSearchResultsBox = document.getElementsByClassName('pac-container')[0];
gmapSearchResultsBox.style.zIndex = '2000';
}
}
componentWillUnmount() {
this.isUnmounted = true;
}
isUnmounted = false;
handleMapMounted = this.handleMapMounted.bind(this);
handleBoundsChanged = this.handleBoundsChanged.bind(this);
handleSearchBoxMounted = this.handleSearchBoxMounted.bind(this);
handlePlacesChanged = this.handlePlacesChanged.bind(this);
handleMarkerClose = this.handleMarkerClose.bind(this);
handleMarkerClick = this.handleMarkerClick.bind(this);
handleMapMounted(map) {
this._map = map;
}
handleBoundsChanged() {
this.setState({
bounds: this._map.getBounds(),
center: this._map.getCenter(),
});
}
handleSearchBoxMounted(searchBox) {
this._searchBox = searchBox;
}
handleLoadPlaces(markers) {
}
handlePlacesChanged() {
const places = this._searchBox.getPlaces();
// Add a marker for each place returned from search bar
const markers = places.map(place => ({
position: place.geometry.location,
}));
// Set markers; set map center to first search result
const mapCenter = markers.length > 0 ? markers[0].position : this.state.center;
this.setState({
center: mapCenter,
markers,
});
this.props.handleLocationChange(mapCenter, places);
}
// Toggle to 'true' to show InfoWindow and re-renders component
handleMarkerClick(targetMarker) {
this.setState({
markers: this.state.markers.map(marker => {
if (marker === targetMarker) {
if (marker.showInfo) {
return {
...marker,
showInfo: false,
};
}
return {
...marker,
showInfo: true,
};
}
return marker;
}),
});
}
handleMarkerClose(targetMarker) {
this.setState({
markers: this.state.markers.map(marker => {
if (marker === targetMarker) {
return {
...marker,
showInfo: false,
};
}
return marker;
}),
});
}
render() {
return (
<GoogleMapComponent
containerElement={
<div style={{ height: '100%' }} />
}
mapElement={
<div style={{ height: '100%' }} />
}
center={this.state.center}
onMapMounted={this.handleMapMounted}
onBoundsChanged={this.handleBoundsChanged}
onSearchBoxMounted={this.handleSearchBoxMounted}
bounds={this.state.bounds}
onPlacesChanged={this.handlePlacesChanged}
onMarkerClick={this.handleMarkerClick}
onMarkerClose={this.handleMarkerClose}
markers={this.state.markers}
/>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ }, dispatch);
}
export default connect(null, mapDispatchToProps)(GoogleMapHandlerComponent);
3. Create Google Map Component, this component uses the components of react-google-maps module
'use strict';
import React from 'react';
import IconButton from 'material-ui/IconButton';
import {
withGoogleMap,
GoogleMap,
Circle,
InfoWindow,
Marker
} from 'react-google-maps';
import SearchBox from 'react-google-maps/lib/places/SearchBox';
import GoogleMapMarkerInfoWindow from './gMapMarkerInfoWindow';
import layoutStyle from '../../../constants/layoutStyle';
const INPUT_STYLE = {
boxSizing: 'border-box',
MozBoxSizing: 'border-box',
border: '1px solid transparent',
width: '240px',
height: '32px',
marginTop: '27px',
marginRight: '20px',
padding: '0 12px',
borderRadius: '1px',
boxShadow: '0 2px 6px rgba(0, 0, 0, 0.3)',
fontSize: '14px',
outline: 'none',
textOverflow: 'ellipses',
};
const GoogleMapComponent = withGoogleMap(props => (
<GoogleMap
ref={props.onMapMounted}
defaultZoom={15}
center={props.center}
onBoundsChanged={props.onBoundsChanged}
>
<SearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
controlPosition={google.maps.ControlPosition.TOP_RIGHT}
onPlacesChanged={props.onPlacesChanged}
inputPlaceholder='Search Location'
inputStyle={INPUT_STYLE}
/>
{props.markers.map((marker, index) => (
<Marker
key={index}
position={marker.position}
onClick={() => props.onMarkerClick(marker)}
>
{/*
Show info window only if the 'showInfo' key of the marker is true.
That is, when the Marker pin has been clicked and 'onCloseClick' has been
Successfully fired.
*/}
{marker.showInfo && (marker.pinOnly === null ||
marker.pinOnly === undefined || !marker.pinOnly) && (
<InfoWindow onCloseClick={() => props.onMarkerClose(marker)}>
<GoogleMapMarkerInfoWindow infoContent={marker.infoContent} />
</InfoWindow>
)}
</Marker>
))}
{props.center && (
<Circle
center={props.center}
radius={props.radius}
options={{
fillColor: 'red',
fillOpacity: 0.20,
strokeColor: 'red',
strokeOpacity: 1,
strokeWeight: 1,
}}
/>
)}
</GoogleMap>
));
export default GoogleMapComponent;
4. Use Google Map Handler component whereever you wish to place the map
render() {
const handleLocationChange = this.handleLocationChange;
const mapsComponent = (
<GoogleMapHandlerComponent
location={this.props.location}
handleLocationChange={handleLocationChange.bind(this)}
/>);
return (
<div
id='mapId'
style={{ width: '450px', height: '350px' }}
>
{mapsComponent}
</div>
);
}
Note you have to pass your current location to land user on his current location, else any other location which is default location.
and a callback for handling change in location.
ReactJS google map location change callback will be called by react-google-maps library when there is change in location,
like user searched some location
Also check stateless react components : http://knowledge-cess.com/reactjs-functional-or-stateless-components/