I am trying to convert this class based to functional component based
here is the actual CLASS BASED CODE
import React, { Component } from 'react';
import L from 'leaflet'
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import './App.css';
// Icon properties
var myIcon = L.icon({
iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII',
iconSize: [25, 41],
iconAnchor: [22, 94],
popupAnchor: [-10, -90],
});
export default class Fullmap extends Component {
state = {
location: {
lat: 51.097,
lng: -1.505
},
haveUsersLocation: false,
zoom: 2
}
componentDidMount(){
// Get GeoLocation for setting marker from browser on page load
navigator.geolocation.getCurrentPosition((position) =>{
this.setState({
location:{
lat: position.coords.latitude,
lng: position.coords.longitude
},
haveUsersLocation: true,
zoom: 13
});
}, ()=>{
// If denied location via browser, get via IP
fetch('https://ipapi.co/json')
.then(res => res.json())
.then(location =>{
this.setState({
location:{
lat: location.latitude,
lng: location.longitude
},
haveUsersLocation: true,
zoom: 13
});
})
});
}
render(){
const position = [this.state.location.lat, this.state.location.lng];
return (
<div className="App">
<Map className="Map" center={position} zoom={this.state.zoom}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{this.state.haveUsersLocation ?
<Marker
icon={myIcon}
position={position}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker> : ''
}
</Map>
</div>
);
}
}
This is working fine with no error whereas now when i converted it to below functional based code
import React, { useState, useEffect } from 'react';
import L from 'leaflet'
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import './App.css';
// Icon properties
var myIcon = L.icon({
iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII',
iconSize: [25, 41],
iconAnchor: [22, 94],
popupAnchor: [-10, -90],
});
export default function MyMap() {
const [location, setLocation] = useState({
lat: false,
lng: null,
zoom: 2,
usersLocation: false
});
useEffect(() => {
// Get GeoLocation for setting marker from browser on page load
navigator.geolocation.getCurrentPosition((position) =>{
setLocation([...location,{
lat: position.coords.latitude,
lng: position.coords.longitude,
haveUsersLocation: true,
zoom: 13
}]);
}, ()=>{
// If denied location via browser, get via IP
fetch('https://ipapi.co/json')
.then(res => res.json())
.then(location =>{
setLocation([...location,{
lat: location.coords.latitude,
lng: location.coords.longitude,
haveUsersLocation: true,
zoom: 13
}]);
})
});
});
const position = [location.lat, location.lng];
return (
<div className="App">
<Map className="Map" center={position} zoom={location.zoom}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{this.state.haveUsersLocation ?
<Marker
icon={myIcon}
position={position}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker> : ''
}
</Map>
</div>
);
}
it is giving me error as TypeError: Cannot read property 'state' of undefined on Line No. 54
I would really appreciate if you could help me point out my error and also explain a bit as I'm pretty new to react.
A functional component doesn't have a this to access the state. You can just reference the state variables directly, in your case location
To expand:
in a class component this refers to the class instance, where the state is stored. In a functional component you gain access to the state via the state variable returned from the useState hook
Only class components have access to this. if you would like to get the current state of haveUsersLocation just simply use location.haveUsersLocation
instead of
{this.state.haveUsersLocation ?
<Marker
...
}
use
{location.haveUsersLocation ?
<Marker
...
}
you have used useState const [location, setLocation] = useState so you can get updated state directly from location variable.
Related
I am trying to use google-map-react library to locate the user's current location on Google Maps. I managed to use react-geolocated to get coordinates and fed them into the Latitude and Longitude props in the GoogleMapReact component but it was very far from accurate. I am now seeking an alternative on how to use Google Maps API to get more accurate coordinates and focus on the area on a map where the user is located. Using handleApiLoaded method which I failed to understand how to use to achieve this.
Below is some of the code that I wrote to try and solve this
import React from "react";
import GoogleMapReact from "google-map-react";
import { geolocated } from "react-geolocated";
import LocateMeButton from "./Button";
import "./App.css";
const Marker = () => (
<img
src={
"http://icons.iconarchive.com/icons/paomedia/small-n-flat/256/map-marker-icon.png"
}
alt="MyPin"
/>
);
const Map = props => {
const [state, stateUpdater] = React.useState({
lat: "",
lng: "",
text: ""
});
const getGeolocationCordinates = () => {
return !props.isGeolocationAvailable
? stateUpdater({ text: "unsupported browser" })
: !props.isGeolocationEnabled
? stateUpdater({ text: "Geolocation not enabled" })
: props.coords
? stateUpdater({
lng: props.coords.longitude,
lat: props.coords.latitude
})
: "Getting the location";
};
return (
<div className=" map-container">
<LocateMeButton Click={() => getGeolocationCordinates()} />
<GoogleMapReact
bootstrapURLKeys={{
key: "API_KEY",
language: "en"
}}
yesIWantToUseGoogleMapApiInternals
defaultCenter={{ lat: 0.3211264, lng: 32.5910528 }}
defaultZoom={11}
>
<Marker lat={state.lat} lng={state.lng} />
</GoogleMapReact>
</div>
);
};
export default geolocated({
positionOptions: {
enableHighAccuracy: true
},
userDecisionTimeout: 5000
})(Map);
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<!-- end snippet -->
import React from "react";
import GoogleMapReact from "google-map-react";
import { geolocated } from "react-geolocated";
import LocateMeButton from "./Button";
import "./App.css";
const Marker = () => (
<img
src={
"http://icons.iconarchive.com/icons/paomedia/small-n-flat/256/map-marker-icon.png"
}
alt="MyPin"
/>
);
const Map = props => {
const [state, stateUpdater] = React.useState({
lat: "",
lng: "",
text: ""
});
const getGeolocationCordinates = () => {
return !props.isGeolocationAvailable
? stateUpdater({ text: "unsupported browser" })
: !props.isGeolocationEnabled
? stateUpdater({ text: "Geolocation not enabled" })
: props.coords
? stateUpdater({
lng: props.coords.longitude,
lat: props.coords.latitude
})
: "Getting the location";
};
return (
<div className=" map-container">
<LocateMeButton Click={() => getGeolocationCordinates()} />
<GoogleMapReact
bootstrapURLKeys={{
key: "AIzaSyBtgM1mf0N-Pcsko7Dc5Q2La-K460a9IsA",
language: "en"
}}
yesIWantToUseGoogleMapApiInternals
defaultCenter={{ lat: 0.3211264, lng: 32.5910528 }}
defaultZoom={11}
>
<Marker lat={state.lat} lng={state.lng} />
</GoogleMapReact>
</div>
);
};
export default geolocated({
positionOptions: {
enableHighAccuracy: true
},
userDecisionTimeout: 5000
})(Map);
I removed the API key that you included in your question. In the future, make sure that you won't post your API key in a public site to protect it.
You can implement this use case using your browser's HTML5 Geolocation feature along with the Maps JavaScript API.
Here is a sample code and code snippet below that implements this in google-map-react library.
import React, { Component } from "react";
import GoogleMapReact from "google-map-react";
import "./style.css";
class GoogleMaps extends Component {
constructor(props) {
super(props);
this.state = {
currentLocation: { lat: 40.756795, lng: -73.954298 }
};
}
render() {
console.log(this.state.inputRad);
const apiIsLoaded = (map, maps) => {
navigator?.geolocation.getCurrentPosition(
({ coords: { latitude: lat, longitude: lng } }) => {
const pos = { lat, lng };
this.setState({ currentLocation: pos });
}
);
};
return (
<div>
<div style={{ height: "400px", width: "100%" }}>
<GoogleMapReact
bootstrapURLKeys={{
key: "YOUR_API_KEY",
libraries: ["places"]
}}
defaultCenter={{ lat: 40.756795, lng: -73.954298 }}
defaultZoom={10}
center={this.state.currentLocation}
yesIWantToUseGoogleMapApiInternals
onGoogleApiLoaded={({ map, maps }) => apiIsLoaded(map, maps)}
/>
</div>
</div>
);
}
}
export default GoogleMaps;
How can i re render this using useeffect geo value getting updated from parent?
import React from 'react'
import { compose } from 'recompose'
import { withGoogleMap, GoogleMap as ReactGoogleMap } from 'react-google-maps'
import Marker from './Marker'
const GoogleMap = compose(withGoogleMap)(({ geo }) => {
console.log(geo)
return (
<ReactGoogleMap
center={{ lat: geo.latitude, lng: geo.longitude }}
options={{
streetViewControl: false,
fullscreenControl: false,
gestureHandling: 'cooperative',
zoom: 18,
}}
>
<Marker loc={geo} />
</ReactGoogleMap>
)
})
export default GoogleMap
I try to display gmaps with a direction route between 2 points. So I built:
A container class:
import React, { Component } from 'react';
import { DirectionsRenderer } from 'react-google-maps';
import Map from './Map';
class MapContainer extends Component {
constructor(props) {
super(props);
this.state = { directions: null };
}
componentWillMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route(
{
origin: new google.maps.LatLng(41.85073, -87.65126),
destination: new google.maps.LatLng(41.85258, -87.65141),
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
}
render() {
return (
<Map
googleMapURL={`https://maps.googleapis.com/maps/api/js?key=<APIKEY>&v=3.exp&libraries=geometry,drawing,places`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `600px`, width: `100%` }} />}
mapElement={<div style={{ height: `100%` }} />}
directions={this.state.directions}
/>
);
}
}
export default MapContainer;
It detects the REACT lifecycle and fetches the JS code from GMaps API and then passes it down to the Map component:
import React, { Component } from 'react';
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
DirectionsRenderer
} from 'react-google-maps';
import package_ico from '../img/package.png';
const Map = withScriptjs(
withGoogleMap(props => {
const marker_package = (
<Marker
position={{
lat: 41.85073,
lng: -87.65126
}}
icon={package_ico}
/>
);
const marker_destination = (
<Marker
position={{
lat: 41.85258,
lng: -87.65141
}}
/>
);
if (props.directions != null) {
console.log('renderdir');
console.log(props.directions);
return (
<GoogleMap defaultZoom={14} center={{ lat: 41.85073, lng: -87.65126 }}>
{marker_package}
{marker_destination}
{props.directions && (
<DirectionsRenderer directions={props.directions} />
)}
</GoogleMap>
);
} else {
console.log('rendernodirec');
return (
<GoogleMap defaultZoom={14} center={{ lat: 41.85073, lng: -87.65126 }}>
{marker_package}
{marker_destination}
</GoogleMap>
);
}
})
);
export default Map;
The data passes correctly from the MapContainer down to the Map but then it seems like the DirectionsRenderer component that is suppose to manage the result doesn't ingest the data correctly and I get the below error message.
57 Uncaught Fc {message: "not a LatLngBounds or LatLngBoundsLiteral: unknown property f", name: "InvalidValueError", stack: "Error↵ at new Fc (https://maps.googleapis.com/m…3.exp&libraries=geometry,drawing,places:170:4324)"}
message: "not a LatLngBounds or LatLngBoundsLiteral: unknown property f"
name: "InvalidValueError"
What am I doing wrong?
I tried to follow the example: https://tomchentw.github.io/react-google-maps/#directionsrenderer but I would like to avoid using recompose since I found it quite confusing...
Thanks for your feedback.
I wasn't able to reproduce the same error, but it could be the sequence of loading the Google Maps script. Since you are using withScriptJs, your call to google.maps.* should be within the component that is wrapped by withScriptJs, where in your example it is outside. Try moving your componentWillMount function into the Map component as in the example below.
If this resolves the issue, it is a race condition caused by the Google Maps script not being loaded before componentWillMount is fired and google.maps.* would be unavailable.
I have a working example on CodeSandbox here. Most of the code is copied from your examples above. Just put in your API key.
import React, { Component } from "react";
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
DirectionsRenderer
} from "react-google-maps";
class Map extends React.Component {
constructor(props) {
super(props);
this.state = { directions: null };
}
componentWillMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route(
{
origin: new google.maps.LatLng(41.85073, -87.65126),
destination: new google.maps.LatLng(41.85258, -87.65141),
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
}
render() {
const marker_package = (
<Marker
position={{
lat: 41.85073,
lng: -87.65126
}}
/>
);
const marker_destination = (
<Marker
position={{
lat: 41.85258,
lng: -87.65141
}}
/>
);
if (this.state.directions != null) {
console.log("renderdir");
console.log(this.state.directions);
return (
<GoogleMap defaultZoom={14} center={{ lat: 41.85073, lng: -87.65126 }}>
{marker_package}
{marker_destination}
{this.state.directions && (
<DirectionsRenderer directions={this.state.directions} />
)}
</GoogleMap>
);
} else {
console.log("rendernodirec");
return (
<GoogleMap defaultZoom={14} center={{ lat: 41.85073, lng: -87.65126 }}>
{marker_package}
{marker_destination}
</GoogleMap>
);
}
}
}
export default withScriptjs(withGoogleMap(Map));
I'm trying to get the current position on google map. The code is working fine when I get the coordinates, but these coordinates I want to get and google map toom because right now, coordinates are static for google map.
import React, { Component } from 'react';
import GoogleMapReact from 'google-map-react';
import { withGoogleMap, GoogleMap, Marker } from "google-map-react"
import { InfoWindow } from 'google-map-react';
import Geolocation from 'react-geolocation'
const AnyReactComponent = ({ text }) => <div>{ text }</div>;
export default class Map extends Component {
static defaultProps = {
center: { lat: 40.7446790, lng: -73.9485420 },
zoom: 11
}
render() {
return (
<Geolocation
render={({
fetchingPosition,
position: { coords: { latitude, longitude } = {} } = {},
error,
getCurrentPosition
}) =>
<div>
<button onClick={getCurrentPosition}>Get Position</button>
{error &&
<div>
{error.message}
</div>}
<pre>
latitude: {latitude}
longitude: {longitude}
</pre>
<div className='google-map' style={{ height: '80vh', width: '100%' }}>
<GoogleMapReact
defaultCenter={ this.props.center }
defaultZoom={ this.props.zoom }>
<AnyReactComponent
lat={ latitude }
lng={ longitude }
text={ 'Wheres Waldo?' }
/>
</GoogleMapReact>
</div>
</div>}
/>
)
}
}
With Geolocation I get very well coordinates, but these coordinates want to set in:
static defaultProps = {
center: { lat: 40.7446790, lng: -73.9485420 },
zoom: 11
}
You must define a state for your <Map> component, which will store your current coordinates.
Then, you should pass the state to your GoogleMapReact component.
In order to set the state with the coordinates found by the Geolocation services, you can use onSuccess callback (https://www.npmjs.com/package/react-geolocation#onsuccess-function):
<Geolocation
onSuccess={this.geoSuccess}
...
/>
The geoSuccess function is:
geoSuccess = position => {
let coords = {
lat: position.coords.latitude,
lng: position.coords.longitude
}
this.setState({
center: coords
})
};
Demo here:
https://43qv620770.codesandbox.io/
The full code can be found here (the file Map.js):
https://codesandbox.io/s/43qv620770
I am trying to open a infowindow on a specific marker on click, however when i click one it appears that all of them are opening instead, and showing me this error:
React.Children.only expected to receive a single React element child.
This is what my code looks like right now:
import React, { Component } from 'react';
import { GoogleMap, Marker, withGoogleMap, withScriptjs, InfoWindow } from 'react-google-maps'
class Map extends Component {
state = {
isOpen: false
}
handleMarkerClick = () => {
this.setState({ isOpen: true})
}
render() {
return(
<div>
<GoogleMap
defaultZoom={13}
defaultCenter={{ lat: -22.9034778, lng: -43.1264636 }}
>{this.props.markers.map((marker, index) =>(
<Marker
key={index}
position={marker.location}
onClick={this.handleMarkerClick}
>{this.state.isOpen && <InfoWindow onCloseClick={this.handleMarkerClick}/>}
</Marker>
))}
</GoogleMap>
</div>
)
}
}
export default withScriptjs(withGoogleMap(Map))
Start of Edit
I made some changes to try and address the comment, however it isn't working yet, can you give me some hints on what i'm doing wrong, since i made some changes to the top component i will paste it here too:
import React, { Component } from 'react';
import Map from './Map.js'
import List from './List.js'
import escapeRegExp from 'escape-string-regexp'
import sortBy from 'sort-by'
class App extends Component {
state ={
locations:[
{
name: "Paróquia Nossa Senhora das Dores do Ingá",
location: {
lat: -22.9038875,
lng: -43.1252873
},
isOpen:false,
},
{
name: "Museu de Arte Contemporanea de Niteroi",
location: {
lat: -22.9078182,
lng: -43.1262919
},
isOpen:false,
},
{
name: "UFF - Faculdade de Direito",
location: {
lat: -22.9038469,
lng: -43.126024
},
isOpen:false,
},
{
name: "Ponte Rio-Niterói",
location: {
lat: -22.8701,
lng: -43.167
},
isOpen:false,
},
{
name: "Fundação Oscar Niemeyer",
location: {
lat: -22.888533927137285,
lng: -43.12815992250511
},
isOpen:false,
},
{
name: "Campo de São Bento",
location: {
lat: -22.905279,
lng: -43.107759
},
isOpen:false,
}
],
query:''
}
onToggleOpen = (location) => {
this.setState({ isOpen: !this.isOpen })
}
updateQuery = (query) => {
this.setState({ query: query.trim() })
console.log(query)
}
componentDidMount() {}
render() {
const { query, locations } = this.state
let filteredMarkers
if(query) {
const match = new RegExp(escapeRegExp(query), 'i')
filteredMarkers = locations.filter((location) => match.test(location.name))
}else {
filteredMarkers = locations
}
filteredMarkers.sort(sortBy('name'))
return (
<div className="App">
<div style={{height:`5vh`}}>
<input
type='text'
placeholder='Search locations'
value={query}
onChange={(event) => this.updateQuery(event.target.value)}
/>
</div>
<List
markers={filteredMarkers}
/>
<Map
onToggle={this.onToggleOpen}
googleMapURL="https://maps.googleapis.com/maps/api/js?&key=AIzaSyAiqO5W1p5FAFf8RZD11PGigUXSlmVHguQ&v=3"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `80vh` }} />}
mapElement={<div style={{ height: `100%` }} />}
className="Map"
markers={filteredMarkers}
/>
</div>
);
}
}
export default App;
Map.js
import React, { Component } from 'react';
import { GoogleMap, Marker, withGoogleMap, withScriptjs, InfoWindow } from 'react-google-maps'
class Map extends Component {
render() {
return(
<div>
<GoogleMap
defaultZoom={13}
defaultCenter={{ lat: -22.9034778, lng: -43.1264636 }}
>{this.props.markers.map((marker, index) =>(
<Marker
key={index}
position={marker.location}
onClick={() => this.props.onToggle(marker)}
>{marker.isOpen && <InfoWindow onCloseClick={this.ontoggleOpen}>Hello</InfoWindow>}
</Marker>
))}
</GoogleMap>
</div>
)
}
}
export default withScriptjs(withGoogleMap(Map))
The problem i was having with React.Children.only expected to receive a single React element child. was being caused because i didn't set a div inside the infowindow, so simply by adding it this particular problem was solved.
Here is what it used to look like:
<InfoWindow onCloseClick={this.handleMarkerClick}/>
here is what it should look like:
<InfoWindow onCloseClick={()=>this.props.onToggle(marker)}><div>Hello</div></InfoWindow>
or something along these lines.