#react-google-maps/api flickering issue with react 18 - javascript

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

Related

How i draw a route with react-google-maps component?

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

How do I change state in reducer from a click in my component?

I am working on a collaborative project, I wanted to know if I could change the state in my reducer by an onClick event listener or anything in my component.
const defaultState = {
pending: false,
error: null,
trips: {},
activeTrip: null,
privacy: false
}
export const tripReducer = (state = defaultState, action) => {
switch (action.type) {
case TOGGLE_VISIBILITY_ON:
return {
...state,
visibility: true,
}
case TOGGLE_VISIBILITY_OFF:
return {
...state,
visibility: false,
}
}
These are the cases that I am concerned with atm. So I want to change visibility once a person marks their trip public in the activepanel component.
code for activePanel
import React from "react"
import * as s from "./components"
import { connect } from "react-redux"
import moment from "moment"
import PropTypes from "prop-types"
import { TripPropTypes } from "../../propTypes"
import { Button } from "../../../styles/theme/styledComponents"
import { toggleWaypoint } from "../../../redux/actions/trips"
import marker from "../../icons/orange-marker.svg"
import startMarker from "../../icons/green-marker.svg"
import endMarker from "../../icons/black-marker.svg"
import { Link } from "react-router-dom"
class ActiveTripPanel extends React.Component {
constructor(props) {
super(props)
this.state = {
polylines: null,
markers: []
}
}
componentDidMount() {
setTimeout(() => {
this.renderWaypoints()
this.drawPolylines()
}, 500)
}
componentDidUpdate(prevProps) {
if (prevProps.waypoints !== this.props.waypoints) {
this.renderWaypoints()
this.drawPolylines()
}
}
drawPolylines = () => {
if (this.state.polylines !== null) {
this.state.polylines.active.setMap(null)
this.state.polylines.complete.setMap(null)
this.state.polylines.current.setMap(null)
}
let completeIndex = 0
for (let i = 0; i < this.props.waypoints.length; i++) {
if (!this.props.waypoints[i].complete) {
completeIndex = i
break
}
}
const completed = this.props.waypoints.slice(0, completeIndex)
const active = this.props.waypoints.slice(
completeIndex,
this.props.waypoints.length + 1
)
const current = this.props.waypoints.slice(
completeIndex - 1,
completeIndex + 2
)
const completePath = completed.map(waypoint => {
return { lat: waypoint.lat, lng: waypoint.lon }
})
const activePath = active.map(waypoint => {
return { lat: waypoint.lat, lng: waypoint.lon }
})
const currentPath = current.map(waypoint => {
return { lat: waypoint.lat, lng: waypoint.lon }
})
const completePolyline = new window.google.maps.Polyline({
path: completePath,
strokeColor: "#FF0000",
strokeOpacity: 1.0,
strokeWeight: 2
})
const currentPolyline = new window.google.maps.Polyline({
path: currentPath,
strokeColor: "#008000",
stokeOpacity: 1.0,
stokeWeight: 2
})
const activePolyline = new window.google.maps.Polyline({
path: activePath,
strokeColor: "#000000",
strokeOpacity: 1.0,
strokeWeight: 2
})
completePolyline.setMap(window.map)
activePolyline.setMap(window.map)
currentPolyline.setMap(window.map)
this.setState({
polylines: {
active: activePolyline,
complete: completePolyline,
current: currentPolyline
}
})
}
renderWaypoints = () => {
let markers = []
const baseIcon = {
anchor: new window.google.maps.Point(15, 30),
scaledSize: new window.google.maps.Size(30, 30),
labelOrigin: new window.google.maps.Point(15, 13)
}
const icons = {
start: {
url: startMarker,
...baseIcon
},
end: {
url: endMarker,
...baseIcon
},
marker: {
url: marker,
...baseIcon
}
}
this.props.waypoints.map((item, i) => {
const icon =
i === 0
? icons.start
: i === this.props.waypoints.length - 1
? icons.end
: icons.marker
let center = { lat: item.lat, lng: item.lon }
const marker = new window.google.maps.Marker({
position: center,
map: window.map,
icon,
title: item.name,
label: {
text: `${i + 1}`,
color: "white",
fontFamily: "Wals",
fontWeight: "bold"
}
})
markers.push(marker)
})
}
render() {
// console.log(match.params.tripId)
// const publicId = ({ match })
return (
<s.Panel>
{/* <s.PanelHeader>{this.props.trip.name}</s.PanelHeader>
<s.DateLabel>
Start: {moment(this.props.trip.start).format("YYYY-MM-DD")} - End:{" "}
{moment(this.props.trip.end).format("YYYY-MM-DD")}
</s.DateLabel> */}
<Link to={`/public/${this.props.trip.id}`}>Share Trip</Link>
<s.WaypointTracker>
{this.props.waypoints &&
this.props.waypoints.map(waypoint => (
<s.WaypointStepper key={waypoint.id}>
<div>
<h4>{waypoint.name}</h4>
<div>
ETA: {moment(waypoint.start).format("YYYY-MM-DD HH:mm")}
</div>
<div>
Status: Checked In #{" "}
{moment(waypoint.start).format("HH:mm")}
</div>
</div>
<div>
{waypoint.complete ? (
<Button
onClick={() => this.props.toggleWaypoint(waypoint.id)}
>
<i className="fa fa-check" />
</Button>
) : (
<Button
onClick={() => this.props.toggleWaypoint(waypoint.id)}
>
<i className="fa fa-times" />
</Button>
)}
</div>
</s.WaypointStepper>
))}
</s.WaypointTracker>
</s.Panel>
)
}
}
ActiveTripPanel.propTypes = {
trip: TripPropTypes,
waypoints: PropTypes.array.isRequired,
toggleWaypoint: PropTypes.func.isRequired
}
const mapStateToProps = ({ trips }) => ({
trip: trips.activeTrip,
waypoints: trips.activeTrip && trips.activeTrip.waypoints,
visibility: trips.visibility
})
export default connect(
mapStateToProps,
{ toggleWaypoint }
)(ActiveTripPanel)
I want to know if its possible or if theres another way to go about it. I am novice at redux. Thank you for the help.
I read the question's text, not the code (long... :)
It is very common (probably the most common way) to change the reducer's state following an event (e.g. onClick):
the onClick event calls an action creator
The action creator returns an action
The action is handled by the reducer
The reducer changes the state.
Does this make sense? I am not sure I understand your question, since this is a standard flow in redux.
BTW, you might want to read an excellent article which helped me when I started learning redux: https://www.sohamkamani.com/blog/2017/03/31/react-redux-connect-explained/

How to implement geolocation in a react app with Google Maps JS API instead of hard-coded location?

I have a React app which uses Google Maps API. I am using Foursquare API also, to fetch data about venues. Currently i am fetching about venues near Nashville, TN, keywords "yoga" and "coffee". I want to use the user's current location, and Nashville as a fallback in case they do not allow.
i've got this from MDN:
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
function success(pos) {
var crd = pos.coords;
console.log('Your current position is:');
console.log(`Latitude : ${crd.latitude}`);
console.log(`Longitude: ${crd.longitude}`);
console.log(`More or less ${crd.accuracy} meters.`);
}
function error(err) {
console.warn(`ERROR(${err.code}): ${err.message}`);
}
navigator.geolocation.getCurrentPosition(success, error, options);
and am looking for help implementing this in my code. How do i start with replacing the near: "Nashville, TN", below with the geolocation code? This is my app.js:
import React, { Component } from 'react';
import './App.css';
import SquareAPI from './API/';
import Map from './component/Map';
import SideBar from './component/Sidebar';
class App extends Component {
constructor(){
super();
this.state = {
venues: [],
markers: [],
center: [],
zoom: 14,
updateSuperState: obj => {
this.setState(obj);
}
};
}
closeAllMarkers = () => {
const markers = this.state.markers.map(marker => {
marker.isOpen = false;
return marker;
});
this.setState({ markers: Object.assign(this.state.markers, markers) });
};
handleMarkerClick = marker => {
this.closeAllMarkers();
marker.isOpen = true;
this.setState({ markers: Object.assign(this.state.markers, marker) });
const venue =this.state.venues.find(venue => venue.id === marker.id);
SquareAPI.getVenueDetails(marker.id).then(res => {
const newVenue = Object.assign(venue, res.response.venue);
this.setState({ venues: Object.assign(this.state.venues, newVenue) })
console.log(newVenue);
});
};
handleListItemClick = venue =>{
const marker = this.state.markers.find(marker => marker.id === venue.id)
this.handleMarkerClick(marker)
}
componentDidMount(){
SquareAPI.search({
near:"Nashville, TN",
query: "yoga",
limit: 10
}).then(results => {
const { venues } = results.response;
const { center } = results.response.geocode.feature.geometry;
const markers = venues.map(venue => {
return {
lat: venue.location.lat,
lng: venue.location.lng,
isOpen: false,
isVisible: true,
id: venue.id
};
})
this.setState({ venues, center, markers });
}).catch(error =>{
console.log("Error: " + error)
})
}
render() {
return (
<div className="App">
<SideBar {...this.state} handleListItemClick={this.handleListItemClick}/>
<Map {...this.state}
handleMarkerClick={this.handleMarkerClick}/>
</div>
);
}
}
export default App;
and my Map.js - i may also need to do it at line 10, defaultCenter=...
/* global google */
import React, { Component } from 'react';
import { withScriptjs, withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps';
const MyMapComponent = withScriptjs(
withGoogleMap(props => (
<GoogleMap
defaultZoom={8}
zoom={props.zoom}
defaultCenter={{ lat: -36.186, lng: -87.066 }}
// defaultCenter={
// }
center={{
lat: parseFloat(props.center.lat),
lng: parseFloat(props.center.lng)
}}
>
{props.markers &&
props.markers.filter(marker => marker.isVisible).map((marker, idx, arr) => {
const venueInfo = props.venues.find(venue => venue.id === marker.id);
return (
<Marker
key={idx}
position={{ lat: marker.lat, lng: marker.lng }}
onClick={() => props.handleMarkerClick(marker)}
animation={arr.length === 1
? google.maps.Animation.BOUNCE
: google.maps.Animation.DROP}
>
{marker.isOpen &&
venueInfo.bestPhoto && (
<InfoWindow>
<React.Fragment>
<img src={`${venueInfo.bestPhoto.prefix}300x300${venueInfo.bestPhoto.suffix}`} alt={venueInfo.name} />
<p>{venueInfo.name}</p>
</React.Fragment>
</InfoWindow>
)}
</Marker>
);
})}
</GoogleMap>
))
);
export default class Map extends Component {
render() {
return (
<MyMapComponent
{...this.props}
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=API_REMOVED"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%`, width: `65%` }} />}
mapElement={<div style={{ height: `100%`}} />}
/>
);
}
}
thanks!
Use the browsers geolocation.
There is an example in the docs.
In terms of React, you would set locations to state, (add a field), pass them to the Map component via prop.
Something like this
class Anything extends Component{
state = {
location : ''
} //no need for constructor no more, these are called class fields.
getPosition= ()=> {
console.log(navigator.gelocation)
//look at example in the docs and then
this.setState(response from navigator)
}
render(){
return (
<Map {...this.state}> // as you are spreading you are good here, access to
// geolocation via this.props.location in map
// component
)
}
}
https://developers.google.com/maps/documentation/javascript/geolocation

Target container is not a DOM element with react google maps with markers/infowindow

What I am trying to do:
Be able to click a marker and have it show the InfoWindow and then be able to click the marker again to close it or just close the pop up window manually. Also want to be able to open multiple markers at once.
What works:
I can click to open InfoWindow and click again to close the window. If I close one marker then I can open another one.
What doesn't work:
If one marker is already open, and I try to click another to open it I get:
Target container is not a DOM element
The above error occurred in the <InfoWindow> component:
This happens only when I click another marker if another marker is already open. I will post the code below. I have seperated out the MapComponent into a separate file.
MapComponent:
import React from 'react';
import { compose, withProps } from 'recompose';
import { withScriptjs, withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps';
import config from '../config';
import { MarkerInfo } from '../components/view';
export const MapComponent = compose(
withProps({
googleMapURL: `https://maps.googleapis.com/maps/api/js?key=${config.GOOGLE_API_KEY}&v=3.exp&libraries=geometry,drawing,places`,
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `500px` }} />,
mapElement: <div style={{ height: `100%` }} />
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap defaultZoom={8} center={props.center}>
{props.isMarkerShown &&
props.center && (
<Marker position={props.center} title={"User's Location"} onClick={props.onHomeMarkerClick}>
<InfoWindow>
<div>User's Location</div>
</InfoWindow>
</Marker>
)}
{props.markers.map((marker, i) => {
const onClick = () => props.onMarkerClick(marker);
const onCloseClick = () => props.onCloseClick(marker);
return (
<Marker key={i} position={marker.position} title={marker.title} onClick={onClick}>
{marker.showInfo && (
<InfoWindow onCloseClick={onCloseClick}>
<div>
<MarkerInfo marker={marker} />
</div>
</InfoWindow>
)}
</Marker>
);
})}
</GoogleMap>
));
React Component Container:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import geolib from 'geolib';
import { geolocated } from 'react-geolocated';
import axios from 'axios';
import actions from '../../actions';
import { MapComponent, Geocode } from '../../utils';
class GhostMap extends Component {
constructor(props) {
super(props);
this.state = {
isMarkerShown: true,
currentLocation: null,
radius: 100,
markers: []
};
this.handleMarkerClick = this.handleMarkerClick.bind(this);
this.handleCloseClick = this.handleCloseClick.bind(this);
this.handleHomeMarkerClick = this.handleHomeMarkerClick.bind(this);
this.createMarkersWithinRadius = this.createMarkersWithinRadius.bind(this);
this.updateRadius = this.updateRadius.bind(this);
}
componentDidMount() {
if (this.props.posts.all) {
return;
}
console.log(this.props);
this.props
.fetchPosts()
.then(data => {
return data;
})
.catch(err => {
console.log(err);
});
}
componentWillReceiveProps(props) {
if (props.coords) {
this.setState({
currentLocation: {
lat: props.coords.latitude,
lng: props.coords.longitude
}
});
this.createMarkersWithinRadius(
props.posts.all,
this.state.radius,
props.coords.latitude,
props.coords.longitude
)
.then(data => {
this.setState({
markers: data
});
return data;
})
.catch(err => {
console.log(err);
});
} else {
axios
.get('http://ip-api.com/json')
.then(response => {
this.setState({
currentLocation: {
lat: response.data.lat,
lng: response.data.lon
}
});
return this.createMarkersWithinRadius(
props.posts.all,
this.state.radius,
response.data.lat,
response.data.lon
);
})
.then(data => {
this.setState({
markers: data
});
})
.catch(err => {
console.log(err);
});
}
}
createMarkersWithinRadius(posts, radius, lat, lng) {
return new Promise((resolve, reject) => {
const markers = [];
const currentLocation = {
latitude: lat,
longitude: lng
};
posts.map(post => {
let postGeolocation = {};
Geocode(post.address, post.city, post.state, post.zipCode)
.then(response => {
postGeolocation.lat = response.lat;
postGeolocation.lng = response.lng;
const distanceArr = geolib.orderByDistance(currentLocation, [postGeolocation]);
const miles = (distanceArr[0].distance / 1609.34).toFixed(2);
if (miles <= radius) {
markers.push({
id: post.id,
position: postGeolocation,
title: post.title,
description: post.text,
image: post.image,
showInfo: false
});
}
resolve(markers);
})
.catch(err => {
reject(err);
});
});
});
}
handleMarkerClick(targetMarker) {
this.setState({
markers: this.state.markers.map(
marker =>
marker.id === targetMarker.id ? { ...marker, showInfo: !marker.showInfo } : marker
)
});
}
handleCloseClick(targetMarker) {
this.setState({
markers: this.state.markers.map(marker => {
if (marker._id === targetMarker._id) {
return {
...marker,
showInfo: false
};
}
return marker;
})
});
}
handleHomeMarkerClick() {
this.setState({ isMarkerShown: false });
}
updateRadius(event) {
const radius = event.target.value;
const posts = this.props.posts.all;
const { lat, lng } = this.state.currentLocation;
if (typeof radius !== 'number') {
alert('Please put in a number');
return;
}
if (this.props.posts.all && this.state.currentLocation) {
this.createMarkersWithinRadius(this.props.posts.all, radius, lat, lng);
}
}
render() {
if (this.state.markers.length === 0) {
return <div>Loading...</div>;
}
return (
<div className="row">
<div className="col-sm-4">
<div className="form-group">
<label htmlFor="text">Radius</label>
<input
onChange={this.updateRadius}
className="form-control"
id="radius"
type="Number"
placeholder="Radius"
/>
</div>
</div>
<div className="col-sm-12">
<MapComponent
onMarkerClick={this.handleMarkerClick}
isMarkerShown={this.state.isMarkerShown}
onHomeMarkerClick={this.handleHomeMarkerClick}
center={this.state.currentLocation}
markers={this.state.markers}
onCloseClick={this.handleCloseClick}
/>
</div>
</div>
);
}
}
const stateToProps = state => {
return {
posts: state.post
};
};
const dispatchToProps = dispatch => {
return {
fetchPosts: () => dispatch(actions.fetchPosts())
};
};
const loadData = store => {
return store.dispatch(actions.fetchPosts());
};
export default {
loadData: loadData,
component: connect(stateToProps, dispatchToProps)(
geolocated({
positionOptions: {
enableHighAccuracy: false
},
userDecisionTimeout: 5000
})(GhostMap)
)
};

google-maps-react user location not working

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

Categories

Resources