Been working with react and still trying to grasp all the concepts. Could use help to understand how I can get this to work. I want to pass an array as props to another react component to map over. Can someone point out what I am doing wrong here? I can map through the array as a function but not inside render:
App.js
import React, { Component } from 'react';
import Leaf from './components/Leaf';
class App extends Component {
constructor(props) {
super(props);
this.state = {
viewport: {
height: "100vh",
width: "100vw",
latitude: 40.7128,
longitude: -74.0060,
zoom: 10
},
latitude: 40.7128,
longitude: -74.0060,
zoom: 10,
stations: [],
selectedStation: null,
userLocation: {}
};
}
componentDidMount() {
fetch('https://gbfs.citibikenyc.com/gbfs/en/station_information.json')
.then(res => res.json())
.then(res=>
this.setState({stations: res}))
}
checkData=()=>{
console.log(this.state.stations)
this.state.stations.data.stations.map(e=>{
console.log(e)
})
}
render() {
return (
<div>
<button onClick={this.checkData}>click me</button>
<Leaf
viewport={this.state.viewport}
stations={this.state.stations}/>
</div>
);
}
}
export default App;
leaf.js
import React, {Component} from 'react';
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import { Button } from 'react-bootstrap';
class leaf extends Component {
checkData=()=>{
this.props.stations.data.stations.map(e=>{
console.log(e)
})
}
render() {
const markers = this.props.stations.data.stations.map((station) =>
<Marker
position={[station.lat, station.lon]}
onClick={this.markerClick.bind(this,station)}>
<Popup>
</Popup>
</Marker>
);
const position = [this.props.viewport.latitude, this.props.viewport.longitude]
//const position = [40.7484, -73.9857]
return (
<div>
<button onClick={this.checkData}>check props</button>
<Map center={position} zoom={14}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{markers}
<Marker position={position}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</Map>
</div>
);
}
}
export default leaf;
In the past I've done something like :
const Search = ( props ) => {
return (
)
}
But I am looking to understand how to make this work using class leaf extends Component . Appreciate the help.
Data Structure for Reference
{
last_updated: 1572631066,
ttl: 10,
data: {
stations: [
{
station_id: "237",
external_id: "66db3c29-0aca-11e7-82f6-3863bb44ef7c",
name: "E 11 St & 2 Ave",
short_name: "5746.04",
lat: 40.73047309,
lon: -73.98672378,
region_id: 71,
rental_methods: [
"CREDITCARD",
"KEY"
],
capacity: 39,
rental_url: "http://app.citibikenyc.com/S6Lr/IBV092JufD?station_id=237",
electric_bike_surcharge_waiver: false,
eightd_has_key_dispenser: false,
eightd_station_services: [
{
id: "e73b6bfb-961f-432c-a61b-8e94c42a1fba",
service_type: "ATTENDED_SERVICE",
bikes_availability: "UNLIMITED",
docks_availability: "NONE",
name: "Valet Service",
description: "Citi Bike Station Valet attendant service available",
schedule_description: "",
link_for_more_info: "https://www.citibikenyc.com/valet"
}
],
has_kiosk: true
},
{
station_id: "281",
external_id: "66db5fae-0aca-11e7-82f6-3863bb44ef7c",
name: "Grand Army Plaza & Central Park S",
short_name: "6839.10",
lat: 40.7643971,
lon: -73.97371465,
region_id: 71,
rental_methods: [
"CREDITCARD",
"KEY"
],
capacity: 66,
rental_url: "http://app.citibikenyc.com/S6Lr/IBV092JufD?station_id=281",
electric_bike_surcharge_waiver: false,
eightd_has_key_dispenser: true,
eightd_station_services: [
{
id: "32461582-cd1e-4ecf-a5ea-563593fa7009",
service_type: "ATTENDED_SERVICE",
bikes_availability: "UNLIMITED",
docks_availability: "NONE",
name: "Valet Service",
description: "Citi Bike Valet Attendant Service Available",
schedule_description: "",
link_for_more_info: "https://www.citibikenyc.com/valet"
}
],
has_kiosk: true
}
]
}
}
Attempts
So here I changed the const markers to mirror the conosle.log from checkData:
const markers = this.props.stations.data.stations.map((station) =>
<Marker
position={[station.lat, station.lon]}
onClick={this.markerClick.bind(this,station)}>
<Popup>
</Popup>
</Marker>
);
I get the following error:
TypeError: Cannot read property 'stations' of undefined
When I remove the markers variable and click checkData, notice that it has not issue mapping over and console logging the object:
I'd doublecheck that you're accessing the correct data properties, as in your function you're calling this.props.stations.data.stations.map, but in your render you're calling this.props.stations.data.map, so one must be incorrect.
Also, class components should be PascalCase, i.e. capitalized.
Camilo was correct and I needed to make sure data was available for render. Below is the working code:
App.js
//import logo from './logo.svg';
//import './App.css';
import React, { Component } from 'react';
import Leaf from './components/Leaf';
class App extends Component {
constructor(props) {
super(props);
this.state = {
viewport: {
height: "100vh",
width: "100vw",
latitude: 40.7128,
longitude: -74.0060,
zoom: 10
},
latitude: 40.7128,
longitude: -74.0060,
zoom: 10,
stations: [],
showStations: false,
selectedStation: null,
userLocation: {}
};
}
componentDidMount() {
const request = async()=> {
await fetch('https://gbfs.citibikenyc.com/gbfs/en/station_information.json')
.then(res => res.json())
.then(res=>
this.setState({stations: res, showStations: true}))
}
request();
}
checkData=()=>{
console.log(this.state.stations)
this.state.stations.data.stations.map(e=>{
console.log(e)
})
}
render() {
return (
<div>
<button onClick={this.checkData}>click me</button>
<Leaf
viewport={this.state.viewport}
stations={this.state.stations}
showStations={this.state.showStations}/>
</div>
);
}
}
export default App;
Leaf.js
import React, {Component} from 'react';
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
//import './leaf.css'
//import InfoBox from './InfoBox';
import { Button } from 'react-bootstrap';
//import Search from './Search';
//import Match from './Match';
//import Modal from './Modal';
//import L from 'leaflet';
//import Routing from "./RoutingMachine";
//import { Row, Col, Grid, Container } from 'react-bootstrap';
//import ErrorBoundary from '../ErrorBoundary/ErrorBoundary'
class Leaf extends Component {
checkData=()=>{
this.props.stations.data.stations.map(e=>{
console.log(e)
})
}
render() {
let markers =null;
if(this.props.showStations) {
markers = (
<div>
{
this.props.stations.data.stations.map((station) =>
<Marker
position={[station.lat, station.lon]}>
</Marker>
)
}
</div>
)
}
const position = [this.props.viewport.latitude, this.props.viewport.longitude]
//const position = [40.7484, -73.9857]
return (
<div>
<button onClick={this.checkData}>check props</button>
<Map center={position} zoom={14}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{markers}
<Marker position={position}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</Map>
</div>
);
}
}
export default Leaf;
Related
Using React Leaflet, I am trying to fire the Marker component's "click" event from a sibling component. In regular leaflet, this is done using something like below:
L.marker(lng,lat).fire('click');
L.marker(lng,lat).openPopup();
In the "Results" component, I have a list of results that when clicked, I would like to trigger the Marker component's events on the map.
render() {
<Results tileClicked2={this.tileClicked3.bind(this)} items={this.state.items}></Results>
<Map ref={m => { this.leafletMap = m; }}>
<TileLayer .../>
{this.state.items.map((value, index) => {
return (<Marker>...</Marker>)
}
In my "tileClicked3" function, I am able to log this.leafletMap
tileClicked3(index){
console.log(index);
console.log(this.leafletMap)
//I want to do:
// marker.fire();
// marker.openPopup();
}
Here is the console log when clicked:
I figure I must be close, but maybe not.
Any advice would be appreciated...
This can be done by keeping the clicked item index in state and getting the marker item at that index and rendering it.
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { Map, TileLayer, Marker } from "react-leaflet";
import "./styles.css";
const listData = [
{
id: 1,
text: "Location A",
lng: 24.960937499999996,
lat: 38.54816542304656
},
{
id: 2,
text: "Location B",
lng: -103.71093749999999,
lat: 40.97989806962013
},
{
id: 3,
text: "Location C",
lng: 76.9921875,
lat: 24.84656534821976
}
];
const List = ({ onListItemClick }) => (
<div>
<ul>
{listData.map((aLocation, index) => (
<li
key={aLocation.id}
onClick={e => {
onListItemClick(index);
}}
>
{aLocation.text}
</li>
))}
</ul>
</div>
);
class App extends Component {
state = {
center: [51.505, -0.091],
zoom: 1,
markerIndex: 0
};
handleItemClick = index => {
console.log("show Marker for", listData[index]);
this.setState({
markerIndex: index
});
};
render() {
const markerItem = listData[this.state.markerIndex];
const MarkerToShow = <Marker position={[markerItem.lat, markerItem.lng]} />;
return (
<div>
<List onListItemClick={this.handleItemClick} />
<Map center={this.state.center} zoom={this.state.zoom}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.osm.org/{z}/{x}/{y}.png"
/>
{MarkerToShow}
</Map>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
You can find the working code here:https://codesandbox.io/s/mm6nmw9z29
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
React/Redux newbie here. I have a form input that allows a user to enter a doctor issue. It returns a list of doctors from the server via Redux action, and displays the doctor list and a marker for each doctor's location on the map (react-google-maps).
When I click submit, the list of doctors for the correct issue displays, the map is there, but with no markers. I can get the markers on the map to display ONLY after submitting the form, THEN clicking on a doctor from the list to display their details.
Want: Enter a doctor issue and render both the list of doctors and markers on the map when the user clicks submit. Then, select a doctor to see their details page (that's another question, routing to dynamic a detail page).
I think, I need to use a life-cycle method but not sure how to implement it. Or, is there a better way to handle this with Redux?
Doctor component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import DoctorSearchForm from '../../containers/doctors/DoctorSearchForm';
import DoctorList from './DoctorList';
import Map from '../maps/Map';
class Doctors extends Component {
constructor(props) {
super(props);
this.state = {
markers: [],
isMarkerShown: false
}
}
componentDidMount() {
this.getMarkers();
}
getMarkers = () => {
let practices = this.props.doctors.map(function(doctor, index) {
return {
title: doctor.profile.first_name + ' ' + doctor.profile.last_name,
location: {
lat: doctor.practices[0].visit_address.lat,
lng: doctor.practices[0].visit_address.lon
}
}
});
this.setState({ markers: practices, isMarkerShown: true });
}
render() {
const { doctors, match } = this.props;
return (
<div>
<DoctorSearchForm getMarkers={this.getMarkers} />
<div className="row">
<div className="col-md-4">
<DoctorList doctors={doctors} match={match} />
</div>
<div className="col-md-8">
<Map
isMarkerShown={this.state.isMarkerShown}
center={{ lat: 45.6318,lng: -122.6716 }}
zoom={12}
markers={this.state.markers}
/>
</div>
</div>
</div>
);
}
}
Doctors.propTypes = {
doctors: PropTypes.array.isRequired,
match: PropTypes.object.isRequired
}
export default Doctors;
DoctorList component:
import React from "react";
import { Route } from 'react-router-dom';
import DoctorItem from './DoctorItem';
import DoctorView from './DoctorView';
class DoctorList extends React.Component {
render() {
const { doctors, match } = this.props;
const linkList = doctors.map((doctor, index) => {
return (
<DoctorItem doctor={doctor} match={match} key={index} />
);
});
return (
<div>
<h3>DoctorList</h3>
<ul>{linkList}</ul>
<Route path={`${match.url}/:name`}
render={ (props) => <DoctorView data= {this.props.doctors} {...props} />}
/>
</div>
);
}
}
export default DoctorList;
DoctorItem component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link, Route } from 'react-router-dom';
import DoctorView from './DoctorView';
const DoctorItem = (props) => {
const { doctor, match } = props;
return (
<li>
<Link to={{ pathname: `${match.url}/${doctor.profile.first_name}-${doctor.profile.last_name}` }}>
{doctor.profile.first_name} {doctor.profile.last_name}
</Link>
</li>
)
}
DoctorItem.propTypes = {
doctor: PropTypes.object.isRequired,
};
export default DoctorItem;
DoctorView component:
import React from 'react';
const DoctorView = ({ data, match }) => {
const doctor = data.find(p => `${p.profile.first_name}-${p.profile.last_name}` === match.params.name);
let doctorData;
if (doctor) {
const mappedSpecialties = Object.entries(doctor.specialties).map(([index, specialty]) => {
return <li key={index} id={index}>{specialty.description}</li>;
});
doctorData =
<div>
<h5><strong>{doctor.profile.first_name} {doctor.profile.last_name}</strong> - {doctor.profile.title}</h5>
<img src={doctor.profile.image_url} alt={"Dr." + doctor.profile.first_name + " " + doctor.profile.last_name} />
<ul>{mappedSpecialties}</ul>
<p>{doctor.profile.bio}</p>
</div>;
}
return (
<div>
{doctorData}
</div>
)
}
export default DoctorView;
Map component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from 'react-google-maps';
import { compose, withProps } from "recompose";
export default Map = compose(
withProps({
googleMapURL:
"https://maps.googleapis.com/maps/api/js?key={MY_KEY}&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap defaultZoom={9} defaultCenter={{ lat: 45.6318,lng: -122.6716 }}>
{props.markers.map((doctor, index) => {
const marker = {
position: { lat: doctor.location.lat, lng: doctor.location.lng },
title: doctor.title
}
return <Marker key={index} {...marker} />;
})}
</GoogleMap>
));
I've spent several days trying and searching for answers but no luck. Any help would be greatly appreciated!
Just like you calculate the markers when the component mounts, you need to recalculate your markers when you receive new props:
componentWillReceiveProps(nextProps) {
this.getMarkers(nextProps);
}
This will require you to change your getMarkers signature a bit so that it can accept an argument and use that instead of this.props in your map operation:
getMarkers = (props = this.props) => {
let practices = props.doctors.map(function(doctor, index) {
return {
title: doctor.profile.first_name + ' ' + doctor.profile.last_name,
location: {
lat: doctor.practices[0].visit_address.lat,
lng: doctor.practices[0].visit_address.lon
}
}
});
this.setState({ markers: practices, isMarkerShown: true });
}
Assuming you are calling getMarkers() in your DoctorSearchForm component, you can remove that since it will automatically update the markers when receiving new props -- or you could bypass state altogether and just calculate it on the fly in render based on the incoming props.
import React from 'react';
import { MapView } from 'expo';
import db from '../db';
import { createRouter, NavigationProvider, StackNavigation } from '#expo/ex-navigation';
import selectedPhototag from '../screens/selectedPhototag';
import { StackNavigator, NavigationActions } from 'react-navigation';
const nav = StackNavigator({
phototag: {
screen: selectedPhototag,
},
});
export default class MapScreen extends React.Component {
state = {
markers: [],
};
componentWillMount() {
db.child('phototags').once('value').then(photoTags => {
let dataArray = [];
for (var key in photoTags.val()) {
dataArray.push(photoTags.val()[key]);
}
this.setState({ markers: dataArray }, () => {
// console.log('[MapScreen] data', this.state.markers);
});
});
}
goTophototags() {
NavigationActions.navigate({ routeName: nav.phototag });
}
render() {
return (
<MapView
provider={MapView.PROVIDER_GOOGLE}
style={{ flex: 1 }}
initialRegion={{
latitude: 40.750355960509054,
longitude: -73.97669815393424,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}>
{this.state.markers.map((marker, i) =>
<MapView.Marker
key={i}
coordinate={{
latitude: marker.locationLat,
longitude: marker.locationLong,
}}
title={marker.description}
onPress={this.goTophototags}
/>
)}
</MapView>
);
}
}
I am trying to navigate to the selectedPhototag screen when one clicks on a marker currently nothing happens on a click the navigation for the rest of the app uses a belt in nav bar so nit sure if I can use those methods, I also want to pass the marker object to the new screen, the app is being made using Expo react Native
1, bind it first:
constructor(props) {
super(props);
this.goTophototags = this.goTophototags.bind(this);
}
2, this case you can call props.navigation.navigate directly:
goTophototags() {
this.props.navigation.navigate('phototag');
}
I'm using the react-google-maps package in React but I'm struggling to get the onDrag event from the component. My code is as follows:
import React, {Component} from 'react';
import GoogleAddressAutocomplete from './googleaddressautocomplete.js';
import { withGoogleMap, GoogleMap, Marker } from "react-google-maps";
import axios from 'axios';
import NavWrapper from './navwrapper.js';
class MapPage extends Component {
constructor(props) {
super(props);
this.state = {
markers: {
position: {
lat: 48.723627,
lng: 21.254199900000003,
},
key: Date.now(),
defaultAnimation: 2,
},
mapCenter: { lat: 48.723627, lng: 21.254199900000003 },
access_token: '',
address: ''
}
}
handleMapClick = this.handleMapClick.bind(this);
handleMapDrag = this.handleMapDrag.bind(this);
handleMapLoad = this.handleMapLoad.bind(this);
handleMapClick(event) {
let that = this;
this.setState({
markers: {
position: event.latLng,
defaultAnimation: 2,
key: Date.now()
},
mapCenter: event.latLng
});
}
handleAddressChange(latLngObject, address) {
console.log('addr: => '+address);
}
handleMapDrag(event) {
console.log(event);
}
handleMapLoad(event) {
console.log(event);
}
render() {
const GoogleMapWrapper = withGoogleMap(props => (
<GoogleMap
ref={props.onMapDrag}
defaultZoom={13}
defaultCenter={props.center}
onClick={props.onMapClick}
onDragEnd={props.onMapDrag}
>
<Marker {...props.markers} />
</GoogleMap>
));
return (
<div className="row-100">
<NavWrapper/>
<GoogleAddressAutocomplete addressChange={this.handleAddressChange.bind(this)} address={this.state.address}/>
<br />
<GoogleMapWrapper
containerElement={
<div style={{ height: `50vh` }} />
}
mapElement={
<div style={{ height: `50vh` }} />
}
onMapClick={this.handleMapClick.bind(this)}
onMapDrag={this.handleMapDrag.bind(this)}
onMapLoad={this.handleMapLoad.bind(this)}
markers={this.state.markers}
center={this.state.mapCenter}
/>
</div>
)
}
}
export default MapPage;
The handleMapDrag(event) function still returns 'undefined'. Could you please help? I need to get the map's center in LatLng format after the map is dragged, either on the onDragEnd event or the onDrag event itself.
Thanks
So the solution was to call the lat&lng functions on the actual instance, note the 'ref' property on the GoogleMaps object, it's important for proper function.
import React, {Component} from 'react';
import GoogleAddressAutocomplete from './googleaddressautocomplete.js';
import { withGoogleMap, GoogleMap, Marker } from "react-google-maps";
import axios from 'axios';
import NavWrapper from './navwrapper.js';
class MapPage extends Component {
constructor(props) {
super(props);
this.state = {
markers: {
position: {
lat: 48.723627,
lng: 21.254199900000003,
},
key: Date.now(),
defaultAnimation: 2,
},
mapCenter: { lat: 48.723627, lng: 21.254199900000003 },
access_token: '',
address: '',
mapRef: null
}
}
componentWillMount() {
}
handleMapClick = this.handleMapClick.bind(this);
handleMapDrag = this.handleMapDrag.bind(this);
handleMapLoad = this.handleMapLoad.bind(this);
handleMapClick(event) {
let that = this;
this.setState({
markers: {
position: event.latLng,
defaultAnimation: 2,
key: Date.now()
},
mapCenter: event.latLng
});
}
handleAddressChange(latLngObject, address) {
console.log('addr: => '+address);
}
handleMapDrag() {
let mapRef = this._mapComponent;
console.log(mapRef.getCenter().lat()+'; '+mapRef.getCenter().lng());
}
handleMapLoad(map) {
this._mapComponent = map;
}
render() {
const GoogleMapWrapper = withGoogleMap(props => (
<GoogleMap
ref={props.onMapLoad}
defaultZoom={13}
defaultCenter={props.center}
onClick={props.onMapClick}
onDragEnd={props.onDragEnd}
>
<Marker {...props.markers} />
</GoogleMap>
));
return (
<div className="row-100">
<NavWrapper/>
<GoogleAddressAutocomplete addressChange={this.handleAddressChange.bind(this)} address={this.state.address}/>
<br />
<GoogleMapWrapper
containerElement={
<div style={{ height: `50vh` }} />
}
mapElement={
<div style={{ height: `50vh` }} />
}
onMapClick={this.handleMapClick}
onDragEnd={this.handleMapDrag}
onMapLoad={this.handleMapLoad}
markers={this.state.markers}
center={this.state.mapCenter}
/>
</div>
)
}
}
export default MapPage;
The actual center of the map is logged to the console in the handleMapDrag() function.
Instead of returning that, return this:
return (
<div className="row-100">
<NavWrapper/>
<GoogleAddressAutocomplete addressChange={this.handleAddressChange.bind(this)} address={this.state.address}/>
<br />
<GoogleMapWrapper
containerElement={
<div style={{ height: `50vh` }} />
}
mapElement={
<div style={{ height: `50vh` }} />
}
onMapClick={this.handleMapClick}
onMapDrag={this.handleMapDrag}
onMapLoad={this.handleMapLoad}
markers={this.state.markers}
center={this.state.mapCenter}
/>
</div>
)
You don't need to bind "this" to the methods again in the events, you just want to call them. You just need to bind 'this' to the methods in the constructor.
Pass this:
handleMapClick = this.handleMapClick.bind(this);
handleMapDrag = this.handleMapDrag.bind(this);
handleMapLoad = this.handleMapLoad.bind(this);
to the constructor.
Not quite sure, I didn't work many times with React, but i think those may be the problems.
Hope it helps.
did you try giving onDragStart to the GoogleMap Component?