I'm traying to draw a route between two points with react-google-maps but is not working for me. Can u help me with this problem? Here's a example of my react component.
import React from 'react';
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
} from 'react-google-maps';
import MapDirectionsRenderer from './app_map_directions_render';
const Map = withScriptjs(
withGoogleMap(props => (
<GoogleMap
defaultCenter={props.defaultCenter}
defaultZoom={props.defaultZoom}
>
{props.places.map((marker, index) => {
const position = {lat: marker.latitude, lng: marker.longitude};
return <Marker key={index} position={position}/>;
})}
<MapDirectionsRenderer places={props.places} travelMode={window.google.maps.TravelMode.DRIVING} />
</GoogleMap>
))
);
const AppMap = props => {
const {places} = props;
const {
loadingElement,
containerElement,
mapElement,
defaultCenter,
defaultZoom
} = props;
return (
<Map
googleMapURL={
'https://maps.googleapis.com/maps/api/js?key=' +
googleMapsApiKey +
'&v=3.exp&libraries=geometry,drawing,places'
}
places={places}
loadingElement={loadingElement || <div style={{height: `100%`}}/>}
containerElement={containerElement || <div style={{height: "80vh"}}/>}
mapElement={mapElement || <div style={{height: `100%`}}/>}
defaultCenter={defaultCenter || {lat: 25.798939, lng: -80.291409}}
defaultZoom={defaultZoom || 11}
/>
);
};
export default AppMap;
And my MapDirectionsRenderer Component
import React, {Component} from 'react';
import { DirectionsRenderer } from "react-google-maps";
export default class MapDirectionsRenderer extends Component {
state = {
directions: null,
error: null
};
componentDidMount() {
const { places, travelMode } = this.props;
const waypoints = places.map(p =>({
location: {lat: p.latitude, lng: p.longitude},
stopover: true
}))
if(waypoints.length >= 2){
const origin = waypoints.shift().location;
const destination = waypoints.pop().location;
const directionsService = new window.google.maps.DirectionsService();
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: travelMode,
waypoints: waypoints
},
(result, status) => {
if (status === window.google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
this.setState({ error: result });
}
}
);
}
}
render() {
if (this.state.error) {
return <h1>{this.state.error}</h1>;
}
return <DirectionsRenderer directions={this.state.directions} />;
}
}
To render a route Google Maps API provides Directions Service, in case of react-google-maps library DirectionsRenderer component is available which is a wrapper around DirectionsRenderer class which in turn:
Renders directions obtained from the DirectionsService.
Assuming the data for route is provided in the following format:
const places = [
{latitude: 25.8103146,longitude: -80.1751609},
{latitude: 27.9947147,longitude: -82.5943645},
{latitude: 28.4813018,longitude: -81.4387899},
//...
]
the following component could be introduced to calculate and render directions via react-google-maps library:
class MapDirectionsRenderer extends React.Component {
state = {
directions: null,
error: null
};
componentDidMount() {
const { places, travelMode } = this.props;
const waypoints = places.map(p =>({
location: {lat: p.latitude, lng:p.longitude},
stopover: true
}))
const origin = waypoints.shift().location;
const destination = waypoints.pop().location;
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: travelMode,
waypoints: waypoints
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
this.setState({ error: result });
}
}
);
}
render() {
if (this.state.error) {
return <h1>{this.state.error}</h1>;
}
return <DirectionsRenderer directions={this.state.directions} />;
}
}
Here is a demo
For React 16.8 or above, MapDirectionsRenderer could be implemented (using Hooks) as below:
function MapDirectionsRenderer(props) {
const [directions, setDirections] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const { places, travelMode } = props;
const waypoints = places.map(p => ({
location: { lat: p.latitude, lng: p.longitude },
stopover: true
}));
const origin = waypoints.shift().location;
const destination = waypoints.pop().location;
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: travelMode,
waypoints: waypoints
},
(result, status) => {
console.log(result)
if (status === google.maps.DirectionsStatus.OK) {
setDirections(result);
} else {
setError(result);
}
}
);
});
if (error) {
return <h1>{error}</h1>;
}
return (
directions && (
<DirectionsRenderer directions={directions} />
)
);
}
Related
I'm using Next.js version 12.3.1 and React.js 18.2.0. When I select point A and point B the direction service and direction renderer gets called and the map prints the direction with the markers. But when I change points A and B the map prints a new direction but the very first direction/line stays on the map.
How do I fix the issue?
Here is the Map functions file:
import React, { useCallback, useState } from "react";
import CustomGoogleMap from "#components/core/CustomGoogleMap";
import { Box } from "#mui/material";
import { anyObjectType } from "#services/types";
import MapAPI from "#services/api/map";
import { CreateBookingActions } from "#services/constants";
import { useCreateBookingContext } from "#contexts/CreateBookingContext";
const BookingMap: React.FC<BookingMapProps> = ({
memoizedloadingPoint,
memoizedunloadingPoint,
memoizedwaypointsList,
setToast,
setLoadingPoint,
setUnLoadingPoint,
}) => {
const cbCtx = useCreateBookingContext();
const { state, dispatch } = cbCtx;
const { waypointsList } = state;
const [call, setCall] = useState(false);
const handleLoadingPointDragLocation = useCallback(
async (lat: number, lng: number) => {
if (lat && lng) {
const query = {
latlng: `${lat},${lng}`,
};
const { response, error }: any = await MapAPI.onDrag(query);
if (response) {
setLoadingPoint({
...response.data.data,
});
} else {
setToast("Somthing went wrong", "error");
}
}
},
[]
);
const handleUnLoadingPointDragLocation = useCallback(
async (lat: number, lng: number) => {
if (lat && lng) {
const query = {
latlng: `${lat},${lng}`,
};
const { response, error }: any = await MapAPI.onDrag(query);
if (response) {
setUnLoadingPoint({
...response.data.data,
});
} else {
setToast("Somthing went wrong", "error");
}
}
},
[]
);
const setWaypointsList = (val: any[]) => {
dispatch({
type: CreateBookingActions.SET_WAYPOINT_LIST,
payload: val,
});
};
const handleStopageDragLocation = useCallback(
async (lat: number, lng: number, index: number) => {
if (lat && lng) {
const query = {
latlng: `${lat},${lng}`,
};
const { response, error }: any = await MapAPI.onDrag(query);
if (response) {
const updatedWaypoints = JSON.parse(JSON.stringify(waypointsList));
updatedWaypoints.splice(index, 1, { ...response.data.data });
setWaypointsList([...updatedWaypoints]);
setCall(true);
} else {
setToast("Somthing went wrong", "error");
}
}
},
[]
);
return (
<Box sx={{ height: "420px" }}>
<CustomGoogleMap
call={call}
setCall={setCall}
loadingP={memoizedloadingPoint}
unloadingP={memoizedunloadingPoint}
waypointsList2={memoizedwaypointsList}
handleLoadingPointDragLocation={handleLoadingPointDragLocation}
handleUnLoadingPointDragLocation={handleUnLoadingPointDragLocation}
handleStopageDragLocation={handleStopageDragLocation}
/>
</Box>
);
};
export default BookingMap;
interface BookingMapProps {
memoizedloadingPoint: anyObjectType;
memoizedunloadingPoint: anyObjectType;
memoizedwaypointsList: any[];
setToast: (a: string, b: string) => void;
setLoadingPoint: (a: anyObjectType) => void;
setUnLoadingPoint: (a: anyObjectType) => void;
}
Here is the google map file:
import React, { useEffect, useState, memo } from "react";
import {
GoogleMap,
MarkerF,
DirectionsRenderer,
DirectionsService,
InfoWindow,
useLoadScript,
LoadScriptNext,
} from "#react-google-maps/api";
import LoadingMarker from "../SvgIcons/loading-marker.svg";
import UnLoadingMarker from "../SvgIcons/unloading-marker.svg";
import StoppageIcon from "../SvgIcons/stopageIcon.svg";
import { CircularProgress } from "#mui/material";
import { Box } from "#mui/system";
import { isEmptyArray } from "#services/utils";
import { anyObjectType, Dispatcher } from "#services/types";
const containerStyle = {
width: "100%",
height: "100%",
};
const center = {
lat: 23.810331,
lng: 90.412521,
};
interface CustomLatLong {
latitude: number;
longitude: number;
textEn: string;
}
type CustomDirectionsWaypoint = google.maps.DirectionsWaypoint & CustomLatLong;
interface CustomGoogleMapProps {
call: Boolean;
setCall: Dispatcher<boolean>;
handleStopageDragLocation?: (a: number, b: number, c: number) => void;
handleLoadingPointDragLocation?: (a: number, b: number) => void;
handleUnLoadingPointDragLocation?: (a: number, b: number) => void;
waypointsList2: CustomDirectionsWaypoint[];
loadingP: anyObjectType;
unloadingP: anyObjectType;
isAddressModal?: Boolean;
}
const CustomGoogleMap: React.FC<CustomGoogleMapProps> = (props) => {
const {
call,
setCall,
loadingP,
unloadingP,
waypointsList2,
handleLoadingPointDragLocation,
handleUnLoadingPointDragLocation,
handleStopageDragLocation,
isAddressModal = false,
} = props;
// const { isLoaded, loadError } = useLoadScript({
// googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!,
// });
const [infoBox, setInfoWindow] = useState({
position: {
lat: 0,
lng: 0,
},
info: "",
});
const [directions, setDirections] = useState(null);
console.log("point:", loadingP, unloadingP);
useEffect(() => {
if (loadingP.latitude && unloadingP.latitude) {
setCall(true);
}
}, [loadingP.latitude, unloadingP.latitude]);
const directionsCallback = (response: any) => {
if (response !== null) {
console.log("directionsCallback called");
if (response.status === "OK") {
setCall(false);
setDirections(response);
}
}
};
const getDirectionRender = () => {
return (
<DirectionsService
options={{
destination: {
lat: unloadingP.latitude,
lng: unloadingP.longitude,
},
origin: {
lat: loadingP.latitude,
lng: loadingP.longitude,
},
travelMode: google.maps.TravelMode.DRIVING,
waypoints: waypointsList2.map(({ latitude, longitude }) => {
return {
location: {
lat: latitude,
lng: longitude,
}.toString(),
stopover: false,
};
}),
}}
callback={directionsCallback}
/>
);
};
// if (loadError) {
// return <div>Map cannot be loaded right now, sorry.</div>;
// }
const renderMap = () => {
return (
<LoadScriptNext
googleMapsApiKey={process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!}
>
<GoogleMap
mapContainerStyle={containerStyle}
center={
loadingP.latitude
? {
lat: loadingP.latitude,
lng: loadingP.longitude,
}
: center
}
zoom={10}
onClick={({ latLng }) => {
isAddressModal &&
handleLoadingPointDragLocation &&
handleLoadingPointDragLocation(latLng!.lat(), latLng!.lng());
}}
>
{loadingP.latitude &&
unloadingP.latitude &&
call &&
getDirectionRender()}
{directions && (
<DirectionsRenderer
options={{
directions: directions,
suppressMarkers: true,
}}
/>
)}
{infoBox.position.lat && (
<InfoWindow
// options={infoBoxOptions}
position={infoBox.position}
onCloseClick={() =>
setInfoWindow({
position: {
lat: 0,
lng: 0,
},
info: "",
})
}
>
<p> {infoBox.info}</p>
</InfoWindow>
)}
<MarkerF
key={loadingP.latitude}
draggable
onDragEnd={({ latLng }) => {
handleLoadingPointDragLocation &&
handleLoadingPointDragLocation(latLng!.lat(), latLng!.lng());
}}
position={{ lat: loadingP.latitude, lng: loadingP.longitude }}
icon={LoadingMarker}
onClick={() =>
setInfoWindow({
...infoBox,
position: {
lat: loadingP.latitude,
lng: loadingP.longitude,
},
info: loadingP.textEn,
})
}
/>
<MarkerF
key={unloadingP.latitude}
draggable
onDragEnd={({ latLng }) => {
handleUnLoadingPointDragLocation &&
handleUnLoadingPointDragLocation(latLng!.lat(), latLng!.lng());
}}
position={{
lat: unloadingP.latitude,
lng: unloadingP.longitude,
}}
icon={UnLoadingMarker}
onClick={() =>
setInfoWindow({
...infoBox,
position: {
lat: unloadingP.latitude,
lng: unloadingP.longitude,
},
info: unloadingP.textEn,
})
}
/>
{!isEmptyArray(waypointsList2) &&
waypointsList2.map((item, index) => {
return (
<MarkerF
key={index}
draggable
visible
onDragEnd={({ latLng }) => {
handleStopageDragLocation &&
handleStopageDragLocation(
latLng!.lat(),
latLng!.lng(),
index
);
}}
position={{
lat: item.latitude,
lng: item.longitude,
}}
icon={StoppageIcon}
onClick={() =>
setInfoWindow({
...infoBox,
position: {
lat: item.latitude,
lng: item.longitude,
},
info: item.textEn,
})
}
/>
);
})}
</GoogleMap>
</LoadScriptNext>
);
};
return renderMap();
};
export default memo(CustomGoogleMap);
I have a code thats will automatically put a marker and route destination if theres a data in the array. But if the array is null its gets an error. How to run a map even if theres no data in my array.
This is my sample code.
googlemap.js
import React, { useState, useEffect } from 'react';
import {
withGoogleMap,
GoogleMap,
withScriptjs,
Marker,
DirectionsRenderer
} from "react-google-maps";
import "./config";
function MapDirectionsRenderer(props) {
const [directions, setDirections] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const { places, travelMode } = props;
const waypoints = places.map(p => ({
location: { lat: p.latitude, lng: p.longitude },
stopover: true
}));
const origin = waypoints.shift().location;
const destination = waypoints.pop().location;
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: travelMode,
waypoints: waypoints
},
(result, status) => {
console.log(result)
if (status === google.maps.DirectionsStatus.OK) {
setDirections(result);
} else {
setError(result);
}
}
);
});
if (error) {
return <h1>{error}</h1>;
}
return (
directions && (
<DirectionsRenderer directions={directions} />
)
);
}
const Map = withScriptjs(
withGoogleMap(props => (
<GoogleMap
defaultCenter={props.defaultCenter}
defaultZoom={props.defaultZoom}
>
{props.markers.map((marker, index) => {
const position = { lat: marker.latitude, lng: marker.longitude };
return <Marker key={index} position={position} />;
})}
<MapDirectionsRenderer
places={props.markers}
travelMode={google.maps.TravelMode.DRIVING}
/>
</GoogleMap>
))
);
export default Map;
Map.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Map from './googlemap';
import "./config";
const googleMapsApiKey = "myapikey";
const places = coordinate;
const App = props => {
const {
loadingElement,
containerElement,
mapElement,
defaultCenter,
defaultZoom
} = props;
return (
<Map
googleMapURL={
'https://maps.googleapis.com/maps/api/js?key=' +
googleMapsApiKey +
'&libraries=geometry,drawing,places'
}
markers={places}
loadingElement={loadingElement || <div style={{height: `100%`}}/>}
containerElement={containerElement || <div style={{height: "80vh"}}/>}
mapElement={mapElement || <div style={{height: `100%`}}/>}
defaultZoom={defaultZoom || 11}
/>
);
};
export default App;
import React, { Component } from 'react';
import { render } from 'react-dom';
import Map from './googlemap';
import "./config";
const googleMapsApiKey = "myapikey";
const places = coordinate;
const App = props => {
const {
loadingElement,
containerElement,
mapElement,
defaultCenter,
defaultZoom
} = props;
return (
<Map
googleMapURL={
'https://maps.googleapis.com/maps/api/js?key=' +
googleMapsApiKey +
'&libraries=geometry,drawing,places'
}
markers={places}
loadingElement={loadingElement || <div style={{height: `100%`}}/>}
containerElement={containerElement || <div style={{height: "80vh"}}/>}
mapElement={mapElement || <div style={{height: `100%`}}/>}
defaultZoom={defaultZoom || 11}
/>
);
};
export default App;
Btw the map will work if there a two routes available if theres 1 or none the map will not show.
I have methods of rendering Markers as shown below. I'm passing markers array from props and rendering it each time componentDidUpdate triggered. The problem is my old markers is not removing from maps. For example if I had 1 coordinates inside my parent component and update it with new ones, the new one appears and the old one stands still.
`
import React from 'react';
const google = window.google;
export class GMap extends React.Component {
mapRef = React.createRef();
directionsService
directionsRenderer
map;
componentDidMount() {
this.initMap();
const { onClick } = this.props;
onClick && this.onMapClick();
}
componentDidUpdate() {
const { markers } = this.props;
this.calcRoute();
if (markers && markers.length > 0) {
this.clear(markers);
this.renderMarkers(markers);
}
}
initMap() {
this.directionsService = new google.maps.DirectionsService();
this.directionsRenderer = new google.maps.DirectionsRenderer();
const mapOptions = {
zoom: 13,
center: { lat: 40.386119, lng: 49.860925 }
}
const map = new google.maps.Map(document.getElementById('map'), mapOptions);
this.map = map;
this.directionsRenderer.setMap(map);
}
onMapClick() {
this.map.addListener('click', (e) => {
this.props.onClick(e);
})
}
renderMarkers(markers) {
markers.forEach(position => {
const marker = new google.maps.Marker({ position });
marker.setMap(this.map);
})
}
calcRoute() {
const { directions } = this.props;
if (directions) {
const [{ lat: fLat, lng: fLng }, { lat: tLat, lng: tLng }] = directions;
if (fLat && fLng && tLat && tLng) {
var request = {
origin: { lat: fLat, lng: fLng },
destination: { lat: tLat, lng: tLng },
travelMode: 'DRIVING'
};
this.directionsService.route(request, (result, status) => {
if (status === 'OK') {
this.directionsRenderer.setDirections(result);
}
});
}
}
}
render() {
return (
<div id='map' ref={this.mapRef} />
)
}
}
`
How are you removing the markers? All I can see in the posted code is this.clear(markers) with no reference to clear. Try doing something like this:
clear(markers) {
for(let i = 0; i < markers.length; i++) {
markers[i].setMap(null);
}
}
Hope this helps!
I have a pretty simple react application using https://github.com/tomchentw/react-google-maps but I'm having difficulty understanding how to get a reference to my current map or how to access the google.maps.Map object in a custom component.
I found this on the repo, but after reading through the posts I'm still a little confused.
I'm starting my application building off of the DirectionsRenderer example.
What I want to do next is add my own custom components for picking the starting point and using the Google Maps autocomplete API.
Yes, I know that the package has a component for that already, but I
need to do a little more than just search for a location on the map.
In order to accomplish my needs I will do something like
const autocomplete = new google.maps.places.Autocomplete(node);
autocomplete.bindTo('bounds', map);
Where node is the element I'm binding the autocomplete functionality and map is an instance of the google.maps.Map object.
My application thus far:
App.jsx
const App = ({ store }) => (
<Provider store={store}>
<div>
<Sidebar>
<StartingPoint defaultText="Choose starting point…" />
</Sidebar>
<GoogleApiWrapper />
</div>
</Provider>
);
GoogleApiWrapper
const GoogleMapHOC = compose(
withProps({
googleMapURL: 'https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=__GAPI_KEY',
loadingElement: <div style={{ height: '100vw' }} />,
containerElement: <div style={{ height: '100vh' }} />,
mapElement: <div style={{ height: '100%' }} />,
}),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
const DirectionsService = new google.maps.DirectionsService();
// make google object available to other components
this.props.onLoad(google);
DirectionsService.route({
origin: new google.maps.LatLng(41.8507300, -87.6512600),
destination: new google.maps.LatLng(41.8525800, -87.6514100),
travelMode: google.maps.TravelMode.DRIVING,
}, (result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result,
});
} else {
console.error(`error fetching directions ${result}`);
}
});
},
}),
)(props => (
<GoogleMap
ref={props.onMapMounted}
defaultZoom={13}
defaultCenter={new google.maps.LatLng(37.771336, -122.446615)}
>
{props.directions && <DirectionsRenderer directions={props.directions} />}
</GoogleMap>
));
If I'm unable to access the google.maps.Map object outside of the wrapper I would alternatively like to access a reference to the element that contains the map so that I may instantiate a new google.maps.Map(ref_to_elem, options);
Any help would be greatly appreciated!
You can do it by React refs:
<GoogleMap ref={(map) => this._map = map} />
function someFunc () {
//using, for example as:
this._map.getCenter()
this._map.setZoom(your desired zoom);
}
import {GoogleMap, withGoogleMap} from 'react-google-maps';
import {MAP} from 'react-google-maps/lib/constants';
const MapComponent = withGoogleMap(() => (
{/*Here you have access to google.maps.Map object*/}
<GoogleMap ref={(map) => map.context[MAP]}/>
));
const Map = ({locations}) => (
<MapComponentClass
containerElement={MapComponent}
mapElement={MapComponent}
locations={locations}/>
);
export default Map;
Worth pointing out for anyone else googling this that nowdays, using react-google-maps you can simply use the useGoogleMap hook to get access to the Google maps instance
https://react-google-maps-api-docs.netlify.app/#map-instance
import React from 'react'
import { useGoogleMap } from '#react-google-maps/api'
function PanningComponent() {
const map = useGoogleMap()
React.useEffect(() => {
if (map) {
map.panTo(...)
}
}, [map])
return null
}
What I did right now in my react-redux application is I assign global variable map outside of react comnponent GoogleMap:
/*global google*/
// your imports //
var map;
class GoogleMap extends Component {
constructor(props) {
super(props);
this.state = {
// your states
};
}
// your functions
componentWillReceiveProps(nextProps) {
}
componentDidMount() {
// code
// render googlemap
map = new google.maps.Map(this.refs.map, yourMapProps);
// add click event listener to the map
map.addListener('click', function(e) {
//code
});
//viewport listener
map.addListener('idle', function(){
// code
});
}
render() {
return (
<div id="map" ref="map">
{places.map((place) => {
return(<Marker place={place} key={place.key} map={map} />);
})}
</div>
}
}
function mapDispatchToProps(dispatch) {
//code
}
export default connect(mapDispatchToProps)(GoogleMap);
Pass map as props into Child Component:
/*global google*/
import React, { Component } from 'react';
class Marker extends Component {
componentDidMount() {
this.renderMarker();
}
renderMarker() {
var { place, map } = this.props;
place.setMap(map);
}
render() {
return null;
}
}
export default Marker;
I don't know is it good practice. Bu it works. I tried to find the solution how to avoid setting Map Object as global windows.map reading all this stuff about singletons and so on. And then this came to my head. Now if I type window.map in the browser concole I get div id="map"
After thoroughly reading through the react-google-maps documentation, examples, and issues I have come to learn that the package does not support a lot of the things I will need to do for my application.
That being said, I have begun writing my own Google Maps API wrapper based off of the work done by Fullstack React. I've omitted a lot of the utilities used in the below mentioned as they can be found here or here.
That being said my solution is to wrap the google maps container in a higher order component and expose the Map Object via the window object:
App
const App = ({ store }) => (
<Provider store={store}>
<div>
<Sidebar>
<StartingPoint />
{/* TODO */}
</Sidebar>
<GoogleMap />
</div>
</Provider>
);
containers/GoogleMap/wrapper.jsx Google Map Higher Order Component wraps GoogleMap Container
const defaultCreateCache = (options) => {
const opts = options || {};
const apiKey = opts.apiKey;
const libraries = opts.libraries || ['places'];
const version = opts.version || '3.24';
const language = opts.language || 'en';
return ScriptCache({
google: GoogleApi({
apiKey,
language,
libraries,
version,
}),
});
};
const wrapper = options => (WrappedComponent) => {
const createCache = options.createCache || defaultCreateCache;
class Wrapper extends Component {
constructor(props, context) {
super(props, context);
this.scriptCache = createCache(options);
this.scriptCache.google.onLoad(this.onLoad.bind(this));
this.state = {
loaded: false,
google: null,
};
}
onLoad() {
this.GAPI = window.google;
this.setState({ loaded: true, google: this.GAPI });
}
render() {
const props = Object.assign({}, this.props, {
loaded: this.state.loaded,
google: window.google,
});
const mapRef = (el) => { this.map = el; };
return (
<div>
<WrappedComponent {...props} />
<div ref={mapRef} />
</div>
);
}
}
Wrapper.propTypes = {
dispatchGoogleAPI: PropTypes.func,
};
Wrapper.defaultProps = {
dispatchGoogleAPI: null,
};
return Wrapper;
};
export default wrapper;
containers/GoogleMap/index.jsx Google Map Container
class Container extends Component {
constructor(props) {
super(props);
this.loadMap = this.loadMap.bind(this);
this.calcRoute = this.calcRoute.bind(this);
}
componentDidUpdate() {
const { origin, destination, route } = this.props;
this.calcRoute(origin, destination);
}
loadMap(node) {
if (this.props && this.props.google) {
const { google } = this.props;
// instantiate Direction Service
this.directionsService = new google.maps.DirectionsService();
this.directionsDisplay = new google.maps.DirectionsRenderer({
suppressMarkers: true,
});
const zoom = 13;
const mapTypeId = google.maps.MapTypeId.ROADMAP;
const lat = 37.776443;
const lng = -122.451978;
const center = new google.maps.LatLng(lat, lng);
const mapConfig = Object.assign({}, {
center,
zoom,
mapTypeId,
});
this.map = new google.maps.Map(node, mapConfig);
this.directionsDisplay.setMap(this.map);
// make the map instance available to other components
window.map = this.map
}
}
calcRoute(origin, destination) {
const { google, route } = this.props;
if (!origin && !destination && !route) return;
const waypts = [];
waypts.push({
location: new google.maps.LatLng(37.415284, -122.076899),
stopover: true,
});
const start = new google.maps.LatLng(origin.lat, origin.lng);
const end = new google.maps.LatLng(destination.lat, destination.lng);
this.createMarker(end);
const request = {
origin: start,
destination: end,
waypoints: waypts,
optimizeWaypoints: true,
travelMode: google.maps.DirectionsTravelMode.DRIVING,
};
this.directionsService.route(request, (response, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.directionsDisplay.setDirections(response);
const route = response.routes[0];
console.log(route);
}
});
this.props.calculateRoute(false);
}
createMarker(latlng) {
const { google } = this.props;
const marker = new google.maps.Marker({
position: latlng,
map: this.map,
});
}
render() {
return (
<div>
<GoogleMapView loaded={this.props.loaded} loadMap={this.loadMap} />
</div>
);
}
}
const GoogleMapContainer = wrapper({
apiKey: ('YOUR_API_KEY'),
version: '3', // 3.*
libraries: ['places'],
})(Container);
const mapStateToProps = state => ({
origin: state.Trip.origin,
destination: state.Trip.destination,
route: state.Trip.route,
});
const mapDispatchToProps = dispatch => ({
dispatchGoogleMap: (map) => {
dispatch(googleMap(map));
},
calculateRoute: (route) => {
dispatch(tripCalculation(route));
},
});
const GoogleMap = connect(mapStateToProps, mapDispatchToProps)(GoogleMapContainer);
export default GoogleMap;
I am trying to display the user location on the map using google-maps-react. I followed the fullstack tutorial, but I just can't seem to display the user location. I will display my Map.js Component below. Please help me point out what I am doing wrong. Thank you.
import React, { Component } from 'react'
import ReactDOM from 'react-dom';
class Map extends Component {
constructor(props) {
super(props);
const {lat, lng} = this.props.initialCenter;
this.state = {
currentLocation: {
lat: lat,
lng: lng
}
}
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.google !== this.props.google) {
this.loadMap();
}
if (prevState.currentLocation !== this.state.currentLocation) {
this.recenterMap();
}
}
recenterMap() {
const map = this.map;
const curr = this.state.currentLocation;
const google = this.props.google;
const maps = google.maps;
if (map) {
let center = new maps.LatLng(curr.lat, curr.lng)
map.panTo(center)
}
}
componentDidMount() {
if (this.props.centerAroundCurrentLocation) {
if (navigator && navigator.geolocation) {
navigator.geolocation.getCurrentPosition((pos) => {
const coords = pos.coords;
this.setState({
currentLocation: {
lat: coords.latitude,
lng: coords.longitude
}
})
})
}
}
this.loadMap();
}
loadMap() {
if (this.props && this.props.google) {
// google is available
const {google} = this.props;
const maps = google.maps;
const mapRef = this.refs.map;
const node = ReactDOM.findDOMNode(mapRef);
let {initialCenter, zoom} = this.props;
const {lat, lng} = initialCenter;
const center = new maps.LatLng(lat, lng);
const mapConfig = Object.assign({}, {
center: center,
zoom: zoom
})
this.map = new maps.Map(node, mapConfig);
}
}
render() {
const style = {
width: '100vw',
height: '100vh'
}
return (
<div ref='map' style={style}>
Loading map...
</div>
)
}
}
Map.propTypes = {
google: React.PropTypes.object,
zoom: React.PropTypes.number,
initialCenter: React.PropTypes.object,
centerAroundCurrentLocation: React.PropTypes.bool
}
Map.defaultProps = {
zoom: 13,
// San Francisco, by default
initialCenter: {
lat: 37.774929,
lng: -122.419416
},
centerAroundCurrentLocation: false
}
export default Map