Im using "google-maps-react" and trying to add new markers to my map with clicks.
I currently manage to console log the specific latLng, but cant seem to make a new one. I'm pretty new to React.
My onMapClick works with finding the latitude and longitude. But I think I need to add the position to an array and then use that one to update the map. Might be wrong
onMapClick = (map,maps,e) => {
const { latLng } = e;
const latitude = e.latLng.lat();
const longitude = e.latLng.lng();
console.log(latitude + ", " + longitude);
var marker = new window.google.maps.Marker({
position: e.latLng,
setMap: map,
});
}
The solution Im currently on is that I just hardcoded some Markers in my render() with the location of array in Marker
<Marker
onClick={this.onMarkerClick}
name={storedLocations[0]}
position={{lat:listLatitude[0], lan:listLongitude[0]}}
/>
My InfoWindow is:
<InfoWindow
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
onClose={this.onClose}
>
<div>
<h4>{this.state.selectedPlace.name}</h4>
</div>
</InfoWindow>
</Map>
My onMapClick works with finding the latitude and longitude. But I
think I need to add the position to an array and then use that one to
update the map.
That's indeed would the React way but instead of instantiating markers via Google Maps API, consider to keep the data (marker locations) via state and React will do the rest like this:
class MapContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
locations: []
};
this.handleMapClick = this.handleMapClick.bind(this);
}
handleMapClick = (ref, map, ev) => {
const location = ev.latLng;
this.setState(prevState => ({
locations: [...prevState.locations, location]
}));
map.panTo(location);
};
render() {
return (
<div className="map-container">
<Map
google={this.props.google}
className={"map"}
zoom={this.props.zoom}
initialCenter={this.props.center}
onClick={this.handleMapClick}
>
{this.state.locations.map((location, i) => {
return (
<Marker
key={i}
position={{ lat: location.lat(), lng: location.lng() }}
/>
);
})}
</Map>
</div>
);
}
}
Explanation:
locations state is used to track all the locations once the map is clicked
locations state is passed to Marker component to render markers
The next step could be to introduce a separate component for markers list, Thinking in React tells about components the follows:
One such technique is the single responsibility principle, that is, a
component should ideally only do one thing. If it ends up growing, it
should be decomposed into smaller subcomponents.
const MarkersList = props => {
const { locations, ...markerProps } = props;
return (
<span>
{locations.map((location, i) => {
return (
<Marker
key={i}
{...markerProps}
position={{ lat: location.lat(), lng: location.lng() }}
/>
);
})}
</span>
);
};
Here is a demo which demonstrates how to add a makers on map click via google-maps-react library
Related
I am currently trying to close my callout when I press the X button of a card on my app.
My code is this. ** function of closing card **
unsetCard = id => {
this.setState({
...this.state,
showCard: false,
});
this.markers.hideCallout();
if (this.state.keyboard) {
Keyboard.dismiss();
}
};
And this is my ** Map View code, I use RN Clustering **
<MapView
//
mapRef={ref => (this.myMapRef = ref)}
//
onPress={this.unsetCard}>
{this.props.data.map(marker => (
<Marker
key={marker.id}
ref={ref => (this.markers = ref)}
//
}>
<Callout
//
}}>
<CustomCallout title={marker.t} />
</Callout>
</Marker>
))}
</MapView>
Finally the function of unset card is called in this component in the same file:
<CustomCardWithImage
close={() => this.unsetCard(this.state.cardInfo.id)}
/>
I would appreciate if someone told me how to use the ref to the marker, because as much as I try it does not work.
Thanks in advance,
After seriously considering not continuing with this app, I took a break and solved the problem. Here is how you can use show or hide callout if you are interested:
Initialize markers
constructor(props) {
super();
this.markers = [];
}
Create markers refs
<Marker
key={marker.id}
ref={ref => {
this.markers[marker.id] = ref;
}}>
Call where needed
this.markers[id].hideCallout();
I hope someone finds it helpful someday
is there a better way to load 1000 markers to the react leaflet map. it takes a while
useEffect(() => {
//get Marker coordinates from db
setMarkerData(data)
}, []);
<Map
center={[0,0]}
maxZoom={18} //shows map
minZoom={3}
id="map"
zoomControl={false}
ref={(e) => {
setMapInstance(e);
}}>
<MarkerClusterGroup>
{MarkerData.map((marker, index) => {
const post = [marker.latitude, marker.longitude];
return (
<Marker
key={index}
position={post}
data={marker}
<Popup>
<strong>{marker.title}</strong>
</Popup>
</Marker>
I am trying to get display the markers on the map with the coordinates fetched from the database. Would it be better to periodically fetch a number of markers at X interval?
I have the map working when it loads, but whenever I pass props the map itself never changes.
The marker changes, but is always 1 state behind what it should be, obviously a sync issue but I'm not sure how to fix this in my component.
class Map extends React.Component {
state = {
center: { lat: 50.937531, lng: 6.960278600000038 }
}
componentWillReceiveProps = () => {
this.setState({
center: {
lat: Number(parseFloat(this.props.city.lat)),
lng: Number(parseFloat(this.props.city.lng))
}
});
}
render() {
console.log(this.state.center)
return (
<div>
<MapDetails
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyB-L-IikkM6BZ_3Z2QIzbkx4pdAkP76tok&v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100vh`}} />}
mapElement={<div style={{ height: `100%` }} />}
mapCenter={this.state.center}
/>
</div>
)
}
}
const mapStateToProps = (state) => {
return { city: state.CityNameReducer }
}
export default connect(mapStateToProps)(Map);
const MapDetails = withScriptjs(withGoogleMap(props =>
<GoogleMap
defaultZoom={12}
defaultCenter={props.mapCenter}
>
<Marker
position={props.mapCenter}
/>
</GoogleMap>
));
The biggest question I have is why the map itself is not updating?
Add a key to GoogleMap component. a Key should be unique each time provided. for your testing use new Date().getTime() function.
<GoogleMap key={new Date().getTime()}/>
as I mentioned its for only testing so make sure to provide this key in better way
I may recommend you to use shouldComponentUpdate(nextProps, nextState) {} instead of componentWillReceiveProps().
I recommend you to read the following page, to understand how it works.
Updating and componentWillReceiveProps
I don't have more information about what is happening in the parent of your component, so I cannot go deeper to the problem. But I think you can get the solution by changing that.
I was wondering if anyone has used google-maps-react library to create Markers and List Items. I have a map component that is rendering the Map, Markers and Info Window. I also have another component that has all List (li) of places that represent each Marker's value. Is there someway that I can click on the list item, and trigger the appropriate Marker's click event.
If List is holding the information of your Markers, you need to uplift state as you are trying to communicate from Child to Parent. So ideally, Markers positions live in the parent, as well as the function to call (Map in your case). And you call it from your list item passing the function via props to the List component like so:
<List handleMarkers={this.function}/>
Access the function in the child via this.props.handleMarkers.
I guess you mean google-maps-react library, right?
Lets say you have a component to select an address:
const CityList = props => {
return (
<div>
<ul>
{props.items.map((item, index) => {
return (
<li key={index} onClick={e => props.onClick(e, item)}>
{item.title}
</li>
);
})}
</ul>
</div>
);
};
Then you could introduce a selected value as state and update the state once the address list item is clicked. Updating the state will trigger a rerender of the Map component:
class App extends Component {
state = {
selectedItem: { lat: 0, lng: 0 }
};
showInfo(e, selectedItem) {
this.setState({ selectedItem: selectedItem });
}
render() {
return (
<div>
<CityList items={data} onClick={this.showInfo.bind(this)} />
<MapContainer
center={{ lat: -24.9923319, lng: 135.2252427 }}
zoom={4}
data={data}
selectedItem={this.state.selectedItem}
/>
</div>
);
}
}
Here is a demo for your reference
I have a map react component and I'm dynamically adding markers.The problem is that when I add markers in the store, the whole map rerenders instead of just appending the markers to the map. Has anyone got any suggestions on how this could be fixed?? I'm pretty sure that I need to Inject the store into CPMap function exclusively, I'm just not sure how.
const CPMap = withGoogleMap((props) => (
<GoogleMap
ref={props.onMapLoad}
style={{
height: 100,
width: 100,
}}
onCenterChanged={props.boundsChanged}
defaultOptions={{ styles: this.mapStyles }}
defaultZoom={props.zoom}
defaultCenter={{ lat: props.center.lat, lng: props.center.lng }}>
<MarkerClusterer
gridSize={40}>
{
props.markers.map(({ key, position }) => (
<Marker
key={key}
position={{ lat: position.lat, lng: position.lng }}
icon={{
url: require('../../images/marker.png')
}}
/>
))
}
</MarkerClusterer>
</GoogleMap >
))
return (
<CPMap
style={{
height: 100,
width: 100,
}}
onMapLoad={(gMap) => {
map = gMap
this.props.map.fetchMarkers()
}}
boundsChanged={() => {
this.props.map.fetchMarkers()
}}
center={this.props.map.center}
zoom={this.props.map.zoom}
markers={mobx.toJS(this.props.map.markers)}
containerElement={
<div style={{ height: 'calc(100vh - 70px)' }
} />
}
mapElement={
<div style={{ height: 'calc(100vh - 70px)' }} />
} />
)
}
I'd propose you the following solution: don't pass makers directly to CMap component, but use store instead;
const markersStore = observable({
markers: []
});
Then in your component - move MarkersClusterer to the separate component and pass markersStore one level deeper:
//import markersStore or inject via mobx-react package
class MyComponent extends Component {
render() {
const CMap = withGoogleMap((props) => (
<GoogleMap
...
<Clusterer markersStore={props.markersStore} />
/>
))
return (
<CMap
...
markersStore={markersStore}
/>
)
}
}
Make your new Cluster component observable so it updates when markersStore "markers" property updates;
#observable
class Clusterer extends Component {
render(){
const { markers } = this.props.markersStore;
return (
<MarkerClusterer>
{ markers.map(...)}
</MarkerClusterer>
)
}
}
The final change is to update your fetchMarkers method so it populates the markersStore with new data.
With this solution CMap component doesn't know anything about markers and it doesn't updates when new data received. At the same time MarkersClusterer component becomes more clever and "observes" changes in the markersStore.