i'd like to use google maps for getting lat and long using redux form. I create this:
import React from 'react';
import { compose, withProps, lifecycle } from 'recompose';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from 'react-google-maps';
const MyMapComponent = compose(
withProps({
googleMapURL:
'https://maps.googleapis.com/maps/api/js?key=AIzaSyCYSleVFeEf3RR8NlBy2_PzHECzPFFdEP0&libraries=geometry,drawing,places',
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
lifecycle({
componentWillMount() {
const refs = {};
this.setState({
position: null,
onMarkerMounted: ref => {
refs.marker = ref;
},
onPositionChanged: () => {
const position = refs.marker.getPosition();
console.log(position.toString());
},
});
},
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap defaultZoom={8} defaultCenter={{ lat: -34.397, lng: 150.644 }}>
{props.isMarkerShown && (
<Marker
position={{ lat: -34.397, lng: 150.644 }}
draggable={true}
ref={props.onMarkerMounted}
onPositionChanged={props.onPositionChanged}
/>
)}
</GoogleMap>
));
class MyParentComponentWrapper extends React.PureComponent {
state = {
isMarkerShown: false,
};
render() {
return (
<div>
<MyMapComponent isMarkerShown={true} />
</div>
);
}
}
export default MyParentComponentWrapper;
But it does not return any values and the does not show the lat and long in the field What should i do? it will console.log the lat and long when user drag the marker
If you want MyMapComponent to return the value to MyParentComponentWrapper just pass a callback function as a prop. Just change the parent component to:
<MyMapComponent isMarkerShown={true} onMakerPositionChanged={this.onMakerPositionChanged}/>
and the map component to:
onPositionChanged: () => {
const position = refs.marker.getPosition();
console.log(position.toString());
this.props.onMakerPositionChanged(position);
},
See working example here
Related
I really don't understand why two infowindows appear after clicking a marker, the weird thing is sometimes in inspection mode infowindow appear normally. I'm wondering if the callback cause the problem. Code are below:
import { GoogleMap, Marker,withGoogleMap,withScriptjs, InfoWindow } from "react-google-maps";
import { nanoid } from 'nanoid'
import React, { Component } from 'react'
const API_KEY = 'INSERT_API_KEY'
const MyMapComponent = withScriptjs(withGoogleMap((props) =>
<GoogleMap
defaultZoom={8}
defaultCenter={{ lat: -33.897, lng: 151.144 }}
>
{props.locs.map((location)=>{
const onMarkerClick = props.onMarkerClick.bind(this,location)
return <Marker
key={nanoid()}
position={location}
onClick={onMarkerClick}>
</Marker>
})}
{props.showingInfoWindow &&
<InfoWindow position={props.activeMarker} onCloseClick={props.markerInfoClose}>
<h1>Details</h1>
</InfoWindow>}
</GoogleMap>
));
export default class Test extends Component {
constructor(props) {
super(props);
this.state = {
locations:[{lat: -33.865143,lng: 151.2},],
showingInfoWindow: false,
activeMarker: {},
};
}
onMarkerClick = (location) =>{
this.setState({
activeMarker: location,
showingInfoWindow: true
});
}
onClose = () => {
if (this.state.showingInfoWindow) {
this.setState({
activeMarker: null,
showingInfoWindow: false
});
}
};
render() {
console.log(this.state.activeMarker)
return (
<div>
<MyMapComponent
locs={this.state.locations}
onMarkerClick={this.onMarkerClick}
showingInfoWindow={this.state.showingInfoWindow}
activeMarker={this.state.activeMarker}
markerInfoClose={this.onClose}
containerElement={ <div style={{ height: `1000px`, width: '1000px' }} /> }
mapElement={ <div style={{ height: `100%` }} /> }
loadingElement={<div style={{ height: `100%` }} />}
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=${API_KEY}`}
>
</MyMapComponent>
</div>
)
}
}
ScreenShoot:
I'm rendering a GoogleMap component using the "react-google-maps" library in React. I can set an initial defaultLocation that works fine when the map initially loads. In the documentation it says that the component takes a "center" prop that one can pass a lat and a lng to but I can't get it to re-render/re-center to a new location. It always shows the default location. Any idea what I am doing wrong?
Is there maybe a way how I can use state outside of the addAdressComponent class so I can dynamically set the initial defaultCenter instead of using props in the render section?
import { withGoogleMap, GoogleMap, Marker } from 'react-google-maps';
/*global google*/
const MapWithMarker = withGoogleMap((props) => (
<GoogleMap
defaultZoom={12}
defaultCenter={{ lat: 47.376888, lng: 8.541694 }}
options={{
disableDefaultUI: true,
}}
>
<Marker position={{ lat: 47.376888, lng: 8.541694 }} />
</GoogleMap>
));
class addAddressComponent extends Component {
constructor(props) {
super(props);
this.state = {
lat: 0,
lng: 0,
};
this.onSuggestSelect = this.onSuggestSelect.bind(this);
}
onSuggestSelect(suggest) {
console.log(suggest.location);
this.setState(() => {
return {
lat: 10.0,
lng: 20.022,
};
});
}
render() {
return (
<div className="wrapperClass">
<MapWithMarker
containerElement={<div style={{ height: '244px' }} />}
mapElement={<div style={{ height: '100%' }} />}
className="mapCSS"
center={(this.state.lat, this.state.lng)}
style={{
width: '348px',
height: '250px',
}}
/>
</div>
);
}
}
export default addAddressComponent;
You'll need to pass the center property to your GoogleMap component inside your MapWithMarker component. Also the center argument needs to be an object like this:
{ lat: -34.397, lng: 150.644 }
The following example lets you change the center of the map using the button:
import logo from './logo.svg';
import './App.css';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from 'react-google-maps';
import {useState} from "react";
const MyMapComponent = withScriptjs(withGoogleMap((props) =>
<GoogleMap
defaultZoom={13}
center={props.center}
defaultCenter={{ lat: -34.397, lng: 150.644 }}
>
{props.isMarkerShown && <Marker position={{ lat: -34.397, lng: 150.644 }} />}
</GoogleMap>
))
function App() {
const [position, setPosition] = useState({ lat: -34.397, lng: 150.644 });
return (
<div className="App">
<button onClick={() => setPosition({lat: 30, lng: 10})}>,
Click me
</button>
<MyMapComponent
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%` }} />}
center={position}
mapElement={<div style={{ height: `1000px`}} />}
/>
</div>
);
}
export default App;
I create a marker like this:
import React from "react";
import config from 'config';
import { compose, withProps, withState, lifecycle } from "recompose";
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
DirectionsRenderer,
Polyline,
} from "react-google-maps";
const googleApiKey = config.googleApiKey;
const HistoryView = compose(
withProps({
googleMapURL: `https://maps.googleapis.com/maps/api/js?key=${googleApiKey}&v=3.exp&libraries=geometry,drawing,places`,
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `345px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withState('zoom', 'onZoomChange', 11),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
const { historyCords } = this.props;
},
componentWillMount() {
this.setState({
zoomToMarkers: map => {
//console.log("Zoom to markers");
const bounds = new google.maps.LatLngBounds();
map.props.children.forEach((child) => {
if (child.type === Marker) {
bounds.extend(new google.maps.LatLng(child.props.position.lat, child.props.position.lng));
}
})
map.fitBounds(bounds);
}
})
},
})
)(props =>
<GoogleMap
//ref={props.zoomToMarkers}
defaultZoom={8}
defaultCenter={new google.maps.LatLng(props.historyCords[0].latitude, props.historyCords[0].longitude)}
center={new google.maps.LatLng(props.latitude, props.longitude)}
zoom={17}
>
{<Polyline path={props.polylineCords}
geodesic={true}
options={{
strokeColor: "#1e9494",
strokeOpacity: 0.75,
strokeWeight: 2,
icons: [
{
icon: "lineSymbol",
offset: "0",
repeat: "20px"
}
]
}}
/>}
{<Marker
options={{icon: {url: "../../public/images/red-mark.svg", scaledSize: new window.google.maps.Size(30, 62)}}}
position={{ lat: props.latitude, lng: props.longitude }}
onClick={props.onMarkerClick} />
}
</GoogleMap>
);
export { HistoryView };
Now how do I move this marker like a car on location updates?
I use states to update the position of the marker but it doesn't animate. How do I do this?
My issue is when a latlng is updated the marker jumps from one place to another but I want it to move like a car. Have you ever tracked an Uber ride on the web? something like that.
Gif for car animation
How to change defaultCenter in react-google-maps ?
I need to find my geolocation and change default values lat and lng.
import React from "react";
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps";
const getLocation = () =>{
const pos = {};
const geolocation = navigator.geolocation;
if (geolocation) {
geolocation.getCurrentPosition(findLocal, showEror);
}
function findLocal(position){
pos.lat = position.coords.latitude;
pos.lng = position.coords.longitude;
}
function showEror(){console.log(Error)}
return pos;
};
const myLocation = getLocation(); // object has lat and lng
I need to transfer my data to the component MapWithAMarker next: Сenter={myLocation} and Marker position={myLocation}
class GoogleMapCenter extends React.Component{
render(){
const MapWithAMarker = withScriptjs(withGoogleMap(props =>
<GoogleMap
defaultZoom={10}
defaultCenter={{ lat: -34.397, lng: 150.644 }}>
{props.isMarkerShown && <Marker position={{ lat: -34.397, lng: 150.644 }} />}
</GoogleMap>
));
return(
<MapWithAMarker
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg&v=3.exp"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
)
}
}
export default GoogleMapCenter;
If I use this.props, it does not work.
<MapWithAMarker
center={this.props.myLocation}
position={this.props.myLocation}
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg&v=3.exp"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
or
<GoogleMap
defaultZoom={10}
defaultCenter={this.props.myLocation}>
{props.isMarkerShown && <Marker position={this.props.myLocation} />}
</GoogleMap>
Default properties like defaultCenter could only be set as initial state, center property could be used instead to re-render (center) the map.
The following example demonstrates how to center the map based on current location
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
currentLatLng: {
lat: 0,
lng: 0
},
isMarkerShown: false
}
}
showCurrentLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
position => {
this.setState(prevState => ({
currentLatLng: {
...prevState.currentLatLng,
lat: position.coords.latitude,
lng: position.coords.longitude
},
isMarkerShown: true
}))
}
)
} else {
error => console.log(error)
}
}
componentDidMount() {
this.showCurrentLocation()
}
render() {
return (
<div>
<MapWithAMarker
isMarkerShown={this.state.isMarkerShown}
currentLocation={this.state.currentLatLng} />
</div>
);
}
}
where
const MapWithAMarker = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withScriptjs,
withGoogleMap
)((props) =>
<GoogleMap
defaultZoom={8}
center={{ lat: props.currentLocation.lat, lng: props.currentLocation.lng }}
>
{props.isMarkerShown && <Marker position={{ lat: props.currentLocation.lat, lng: props.currentLocation.lng }} onClick={props.onMarkerClick} />}
</GoogleMap>
)
Demo (edit)
I'm a fresh starter in React and Apollo (GraphQL). I need to display a Layer when a user clicks on a google maps marker. My Apollo Request is displaying correctly the markers depending on found missions in the database, the only problem I'm facing is that the onClick is returning a TypeError: _this.handleMissionClick is not a function. I think this is because the function is outside the render part and the main class, and can't find a way to link the two parts.
Here is the complete page code :
// #flow
import React from 'react'
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps"
import gql from 'graphql-tag'
import { graphql } from 'react-apollo'
import { connect } from 'react-redux'
import Layer from 'grommet/components/Layer'
import MissionDetails from './../missions/details'
type TMission = {
id : string,
description: string,
status: string,
title: string,
location: number[]
}
type TMissionProps = {
data: {
loading: boolean,
searchMissions?: Array<TMission>
}
}
function setIcon (status) {
switch(status) {
case 'accepted':
return 'http://maps.google.com/mapfiles/ms/icons/green-dot.png';
case 'created':
return 'http://maps.google.com/mapfiles/ms/icons/red-dot.png';
default:
return null;
}
}
const _MissionList = (props: TMissionProps) => {
if (!props.data.searchMissions) return null
return (
props.data.searchMissions &&
props.data.searchMissions.map( mission => (
<Marker
position={{ lng: mission.location[0], lat: mission.location[1]}}
key={ mission.id }
title={ mission.title }
icon={setIcon(mission.status)}
onClick={() => this.handleMissionClick(mission.id)}
>
</Marker>
))
)
}
const MissionList = connect(
({ session }) => ({ t: 1 })
)(graphql(gql`
query mapMissions(
$authToken: String!
) {
searchMissions(
input: {
location: [2, 3]
}
) {
id
title
status
}
}
`, {
options: {
fetchPolicy: 'network-only'
}
})(_MissionList))
const GoogleMapWrapper = withScriptjs(withGoogleMap((props) =>
<GoogleMap
defaultZoom={11}
defaultCenter={{ lat: 48.8588377, lng: 2.2770201 }}
center={props.center}
>
<MissionList/>
</GoogleMap>))
export default class DashboardPage extends React.Component {
constructor () {
super();
this.state = {
localised: {
lat: 48.8588377,
lng: 2.2770201,
selectedMissionId: null,
showMissionDetailsLayer: false
}
};
}
toggleMissionDetailsLayer = () => {
this.setState({
showMissionDetailsLayer: !this.state.showMissionDetailsLayer
})
}
componentDidMount () {
this.getLocation();
}
handleMissionClick = (missionId: string) => {
this.setState({
selectedMissionId: missionId
})
this.toggleMissionDetailsLayer()
}
getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
this.setState({
localised: {
lat: position.coords.latitude,
lng: position.coords.longitude
}}
)
})
}
}
render () {
return (
<div>
<div style={{ height: '20vh', width: '100%' }}>
</div>
<GoogleMapWrapper isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=$$$&?v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `80vh` }} />}
center={{ lat : this.state.localised.lat, lng : this.state.localised.lng }}
/>
{
this.state.showMissionDetailsLayer &&
<Layer align='right' closer={true} overlayClose={true} onClose={this.toggleMissionDetailsLayer}>
<MissionDetails mission={_(this.props.data.allMissions).find({id: this.state.selectedMissionId})} />
</Layer>
}
</div>
)
}
}
Since your handleMissionClick is defined in DashboardPage. Which is one of the parents of MissionList you need to pass the callback handler through the props. That way MissionList has a prop to handle the onClick
In your DashboardPage you should pass an onClickHandler
<GoogleMapWrapper
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=$$$&?v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `80vh` }} />}
center={{ lat : this.state.localised.lat, lng : this.state.localised.lng }}
handleMissionClick={this.handleMissionClick} // Add callback prop here
/>
Inside your GoogleMapWrapper pass the handleMissionClick prop to MissionList
const GoogleMapWrapper = withScriptjs(withGoogleMap((props) =>
<GoogleMap
defaultZoom={11}
defaultCenter={{ lat: 48.8588377, lng: 2.2770201 }}
center={props.center}
>
<MissionList handleMissionClick={props.handleMissionClick} />
</GoogleMap>))
In your MissionProps you now have onClick as a prop
type TMissionProps = {
data: {
loading: boolean,
searchMissions?: Array<TMission>
},
onClick: string => void
}
Update your Marker to use the callback prop
<Marker
position={{ lng: mission.location[0], lat: mission.location[1]}}
key={ mission.id }
title={ mission.title }
icon={setIcon(mission.status)}
onClick={() => props.handleMissionClick(mission.id)} // Notice this is now props.handleMissionClick instead of this.handleMissionClick
>
</Marker>