I am using Google Maps React API. I am not sure what I am doing wrong, but latitude and longitude remain to be 0, 0 when I attempt to update with Axios.
import React, {Component} from 'react'
import {Map, InfoWindow, Marker, GoogleApiWrapper, mapEventHandler, markerEventHandler} from 'google-maps-react';
import axios from 'axios'
const mapStyle = {
width: '100%',
height: '90%'
}
export class MapContainer extends Component<{google}, { map: google.maps.Map<Element>, latitude: number, longitude: number}>{
onMapClicked: mapEventHandler;
onMarkerClick: markerEventHandler;
map?: google.maps.Map | google.maps.StreetViewPanorama
marker?: google.maps.Marker
onInfoWindowClose: any;
constructor(props){
super(props);
this.state = {
map: null,
latitude: 0,
longitude: 0
}
this.componentDidMount = this.componentDidMount.bind(this);
}
componentDidMount(){
axios
.get('https://api.##########.com/admin/user', {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer e-------------IU'
},
params: {
'userId':'c------------------------------------d'
}
})
.then(
resp => {
this.setState({
latitude: Number(resp.data.location.latitude),
longitude: Number(resp.data.location.longitude)
})
console.log("Before:" + this.state.latitude + " and " + this.state.longitude);
})
.catch(err => console.log(err))
}
render(){
console.log("After:" + this.state.latitude + " and " + this.state.longitude);
return(
<>
<Map google={google}
zoom={16}
draggable
initialCenter={{
lat: this.state.latitude,
lng: this.state.longitude
}}
onReady={(mapProps, map) => {
this.setState({ map: map as google.maps.Map})
}}
style={mapStyle}
onClick={this.onMapClicked}>
<Marker onClick={this.onMarkerClick}
title={`Location of ...`} />
<InfoWindow google={google}
map={this.map as google.maps.Map}
marker={this.marker}
visible>
<div>
<h1>Hello.</h1>
</div>
</InfoWindow>
</Map>
<p className="float-left md:ml-32 mt-64 sm:pt-32 lg:pt-32">
</p>
</>
)
}
}
const GoogleMap = GoogleApiWrapper({
apiKey: 'A------w'
})(MapContainer)
export default GoogleMap;
Basically, I have no idea how to use MapContainer / GoogleMaps to take props, so I am using axios within this class to set latitude and longitude to the state. I do have the same longitude and latitude in another folder, so that's an option for me to, but I don't mind doing this for now. However, the coordinates remain at (0, 0). Am I mixing up the order in which I do this or something?
Edit: By the way, it's the map itself that does not update, even if the other states do
This is because setState is asynchronous. So use state after setState may not be updated one.
You have to use callback for setState to get updated value.
Alternatively you can use componentDidUpdate to use updated state value.
So you have to do this:
this.setState(
{
latitude: Number(resp.data.location.latitude),
longitude: Number(resp.data.location.longitude),
},
() => {
console.log("Before:" + this.state.latitude + " and " + this.state.longitude);
}
);
Related
So I have been trying to implement a google maps instance on my NextJS app, I got most things to work however, I want to display the data I'm fetching from an external source as 'markers' on the map.
For this I have been using the following code but the markers dont seem to show up.
import { Map, InfoWindow, Marker, GoogleApiWrapper, showInfoWindow } from 'google-maps-react';
import React, { Component } from 'react';
export class MapContainer extends Component {
constructor(props) {
super(props);
this.state = {
data: null,
showingInfoWindow: false,
activeMarker: {},
selectedPlace: {},
};
}
componentDidMount() {
// make fetch request
fetch('api/twitter/search')
.then(response => response.json())
.then(data => this.setState({ data }));
}
onMarkerClick = (props, marker, e) =>
this.setState({
selectedPlace: props,
activeMarker: marker,
showingInfoWindow: true
});
onMapClicked = (props) => {
if (this.state.showingInfoWindow) {
this.setState({
showingInfoWindow: false,
activeMarker: null
})
}
};
render() {
const { data } = this.state;
console.log(data)
return (
<div>
<Map google={this.props.google}
onClick={this.onMapClicked} width={50} height={50}
center={{
lat: 40.854885,
lng: -88.081807
}}>
{
data?.data.map((st) => (
<>
(
{
st?.place &&
<Marker
title={st?.user.name}
name={'SOMA'}
position={{ lat: 40.854885, lng: -88.081807 }}
key={st?.id} />
}
)
</>
))
}
</Map>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: ('#')
})(MapContainer)
The map shows up but there are no markers, I would like to display all the items fetched from the API into the Map, I'm not sure if I'm going about the right way by using a javascript map to display a number of markers.
What I'm trying to do is send in an array of addresses to a component, use google's geocoder to convert those addresses into lat / long coordinates, and then plat those places on a google map with markers using the google maps api react wrapper. I followed this tutorial pretty closely (https://dev.to/jessicabetts/how-to-use-google-maps-api-and-react-js-26c2) with the biggest difference being that I worked in geocoder. Because geocoder is asynchronous, I can't get the map to re-render with the newly converted coordinates after the promise is resolved. Below is the code I have right now:
import React, { Component } from 'react';
import {Map, InfoWindow, Marker, GoogleApiWrapper} from 'google-maps-react';
const mapStyles = {
width: '100%',
height: '300px'
};
let geocoder;
let addressData = [{location: "146 Pierrepont St, Brooklyn, NY, USA"}, {location: "153 Remsen St, Brooklyn, NY, USA"}];
export class MapContainer extends Component {
constructor (props) {
super(props);
this.onMarkerClick = this.onMarkerClick.bind(this);
this.displayMarkers = this.displayMarkers.bind(this);
this.state = {
lat: 40.6946768,
lng: -73.99161700000002,
showingInfoWindow: false,
activeMarker: {},
selectedPlace: {},
places: [],
stores: [{latitude: 47.49855629475769, longitude: -122.14184416996333},
{latitude: 47.359423, longitude: -122.021071},
{latitude: 47.2052192687988, longitude: -121.988426208496},
{latitude: 47.6307081, longitude: -122.1434325},
{latitude: 47.3084488, longitude: -122.2140121},
{latitude: 47.5524695, longitude: -122.0425407}]
}
}
componentDidMount () {
this.plotPoints()
}
plotPoints () {
let locations = this.getPoints(geocoder)
let places = new Array()
Promise.all(locations)
.then(function(returnVals) {
returnVals.forEach(function(latLng) {
let place = {latitude: latLng[0], longitude: latLng[1]}
places.push(place)
})
})
this.setState (() => {
return {
places: places
}
});
}
getPoints(geocoder) {
let locationData = [];
for (let i = 0; i < addressData.length; i++) {
locationData.push(this.findLatLang(addressData[i].location, geocoder))
}
return locationData // array of promises
}
findLatLang(address, geocoder) {
return new Promise(function(resolve, reject) {
geocoder.geocode({
'address': address
}, function(results, status) {
if (status === 'OK') {
console.log(results);
resolve([results[0].geometry.location.lat(), results[0].geometry.location.lng()]);
} else {
reject(new Error('Couldnt\'t find the location ' + address));
}
})
})
}
displayMarkers (stores) {
return stores.map((place, index) => {
return <Marker key={index} id={index} position={{
lat: place.latitude,
lng: place.longitude
}}
onClick={() => console.log("You clicked me!")} />
})
}
onMarkerClick (props, marker, e) {
this.setState({
selectedPlace: props,
activeMarker: marker,
showingInfoWindow: true
});
};
render() {
geocoder = new this.props.google.maps.Geocoder();
return (
<div className="container place-map">
<div className="row">
<div className="col-md-12">
<Map
google={this.props.google}
zoom={14}
style={mapStyles}
initialCenter={{
lat: this.state.lat,
lng: this.state.lng
}}
>
{this.displayMarkers(this.state.stores)}
{this.displayMarkers(this.state.places)}
<InfoWindow
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
>
<div>Your Location Here!</div>
</InfoWindow>
</Map>
</div>
</div>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: 'AIzaSyCOJDrZ_DXmHzbzSXv74mULU3aMu3rNrQc'
})(MapContainer);
The array of "stores" renders markers on the map since there are coordinates available at the initial render of the map - but the coordinates that get pushed onto the "places" array never render. If I put a log statement of the "places" into render() I can see that I am getting back valid coordinates from geocoder.
Help! Been banging my head on this for forever (as you can tell by the current sloppy state of the code).
You need to move the setState for places into the Promise.all callback.
You are calling it when the array is still empty and before the promises have resolved
Promise.all(locations)
.then((returnVals) =>{
returnVals.forEach((latLng) => {
let place = {
latitude: latLng[0],
longitude: latLng[1]
}
places.push(place)
})
// places now populated
this.setState(() => {
return {
places: places
}
});
});
I have to files: eventsMapPage.js (main) and Maps.js(child).
getEvents = async () => {
const requestResponse = await request(BASE_API_URL + "/api/events", { method: "GET", headers: {}, body: {} });
this.state.eventList = requestResponse.data;
console.log('getEvents');
console.log(this.state.eventList);
}
//fetching data from api in parent
```getEvents = async () => {
const requestResponse =
await request(BASE_API_URL + "/api/events", {method:"GET", headers: {}, body: {} });
this.state.eventList = requestResponse.data;
}
```
//Sent state with fetched data
```
<GoogleApiWrapper eventList={this.state.eventList} ></GoogleApiWrapper>
```
//Send data
```
let markerList = []
export class MapContainer extends Component {
constructor(props) {
super(props);
this.state = {};
markerList = props.eventList;
```
//I want to put this fetched data to Markers
```
return (
<Map google={google} zoom={14} onClick={this.onMapClick} style={mapStyles} initialCenter={initialCenter}>
{
markerList.map(marker => {
return (
<Marker
key={marker.id}
onClick={this.onMarkerClick}
title={marker.title}
name={marker.name}
position={{
lat: marker.lat,
lng: marker.lng
}}
/>
...
```
Actually, I want only to have Markers from web api in my google maps. When I send hard-coded arrar{} with data it works but when I send with this api. First renders child, then takes from api. So I don't have any Markers on my map.
I read about:
a)componentWillMount
b)event on google maps like onChange or onBoundsChanged but I have no idea how to use it in my project.
Normally in WPF I had binding, here google maps works strange. JS should refresh automaticly when data comes. How to have Markers from api?
You are directly mutating the state like,
this.state.eventList = requestResponse.data; //direct mutation
You never mutate state like this, because it is not the right way to change state and it will not re-render your component.
You must use setState to change your state, which will cause a re-render and your component will get data.
this.setState({eventList : requestResponse.data})
Also make sure you are adding your child component when your data is ready,
{this.state.eventList.length > 0 && <GoogleApiWrapper eventList={this.state.eventList} ></GoogleApiWrapper>}
main.js
import React from 'react';
import GoogleMapsWrapper from './GoogleMapsWrapper.js';
import { Marker } from 'react-google-maps';
import MarkerClusterer from "react-google-/lib/components/addons/MarkerClusterer";
class DemoApp extends React.Component {
componentWillMount() {
this.setState({ markers: [] })
}
componentDidMount() {
const url = [
// Length issue
`https://gist.githubusercontent.com`,
`/farrrr/dfda7dd7fccfec5474d3`,
`/raw/758852bbc1979f6c4522ab4e92d1c92cba8fb0dc/data.json`
].join("")
fetch(url)
.then(res => res.json())
.then(data => {
this.setState({ markers: data.photos });
});
}
render () {
return (
<GoogleMapsWrapper
googleMapURL="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }}
defaultZoom={3}
defaultCenter={{ lat: 25.0391667, lng: 121.525 }}>
<MarkerClusterer
averageCenter
enableRetinaIcons
gridSize={60}>
{this.state.markers.map(marker => (
<Marker
key={marker.photo_id}
position={{ lat: marker.latitude, lng: marker.longitude }}
/>
))}
</MarkerClusterer>
</GoogleMapsWrapper>
);
}
}
GoogleMapsWrapper.js
import React from 'react';
import { GoogleMap,withGoogleMap,withScriptjs } from 'react-google-maps';
export default const GoogleMapsWrapper = withScriptjs(withGoogleMap(props => {
return <GoogleMap {...props} ref={props.onMapMounted}>{props.children}</GoogleMap>
}));
Follow https://github.com/tomchentw/react-google-maps/issues/636
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
I'm new to React and currently trying to learn how to use react-google-maps library. Tried to show a map with users geolocation as the initialCenter of the map.
This is my code:
import React from "react";
import { GoogleApiWrapper, Map } from "google-maps-react";
export class MapContainer extends React.Component {
constructor(props) {
super(props);
this.state = { userLocation: { lat: 32, lng: 32 } };
}
componentWillMount(props) {
this.setState({
userLocation: navigator.geolocation.getCurrentPosition(
this.renderPosition
)
});
}
renderPosition(position) {
return { lat: position.coords.latitude, lng: position.coords.longitude };
}
render() {
return (
<Map
google={this.props.google}
initialCenter={this.state.userLocation}
zoom={10}
/>
);
}
}
export default GoogleApiWrapper({
apiKey: "-----------"
})(MapContainer);
Insted of creating a map with users location I get an initialCenter of my default state values.
How can I fix it? Am I even using the lifecycle function right?
Thank you very much for your help
navigator.geolocation.getCurrentPosition is asynchronous, so you need to use the success callback and set the user location in there.
You could add an additional piece of state named e.g. loading, and only render when the user's geolocation is known.
Example
export class MapContainer extends React.Component {
state = { userLocation: { lat: 32, lng: 32 }, loading: true };
componentDidMount(props) {
navigator.geolocation.getCurrentPosition(
position => {
const { latitude, longitude } = position.coords;
this.setState({
userLocation: { lat: latitude, lng: longitude },
loading: false
});
},
() => {
this.setState({ loading: false });
}
);
}
render() {
const { loading, userLocation } = this.state;
const { google } = this.props;
if (loading) {
return null;
}
return <Map google={google} initialCenter={userLocation} zoom={10} />;
}
}
export default GoogleApiWrapper({
apiKey: "-----------"
})(MapContainer);