I've built an application that draws an address from a data base. I've used geocoder (https://www.npmjs.com/package/geocoder) to get the lat and lng of the address and now wish that everytime I hit a button that pulls the address, it will update the map to zoom to that address and centre. Can this be done if I'm pulling the address external to the maps component?
Here's my map component so far:
const MapWithASearchBox = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=AIzaSyDYwerCGhOnkM1sNHmSrckp8D1o9hY3mZ4&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
lifecycle({
componentDidMount(){
},
componentWillMount() {
const refs = {}
this.setState({
bounds: null,
center: {
lat: -35.015742, lng: 138.520858
},
markers: [],
onMapMounted: ref => {
refs.map = ref;
},
onIdle: () => {
this.setState({
center: fullPos,
})
},
onBoundsChanged: () => {
this.setState({
bounds: refs.map.getBounds(),
center: refs.map.getCenter(),
})
},
onSearchBoxMounted: ref => {
console.log("s. mounted");
refs.searchBox = ref;
},
onPlacesChanged: () => {
console.log("places changed");
console.log(refs.searchBox)
console.log(refs)
const places = refs.searchBox.getPlaces();
console.log(places);
const bounds = new google.maps.LatLngBounds();
places.forEach(place => {
if (place.geometry.viewport) {
bounds.union(place.geometry.viewport)
} else {
bounds.extend(place.geometry.location)
}
});
const nextMarkers = places.map(place => ({
position: place.geometry.location,
}));
const nextCenter = _.get(nextMarkers, '0.position', this.state.center);
this.setState({
center: nextCenter,
markers: nextMarkers,
});
// refs.map.fitBounds(bounds);
},
})
},
}),
withScriptjs,
withGoogleMap
)(props =>
<GoogleMap
ref={props.onMapMounted}
defaultZoom={20}
center={props.center}
onBoundsChanged={props.onBoundsChanged}
mapTypeId={google.maps.MapTypeId.SATELLITE}
panControl={true}
scrollwheel={false}
>
<SearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
controlPosition={google.maps.ControlPosition.TOP_LEFT}
onPlacesChanged={props.onPlacesChanged}
>
<input
type="text"
placeholder="Customized your placeholder"
value={fullAdd}
style={{
boxSizing: `border-box`,
border: `1px solid transparent`,
width: `240px`,
height: `32px`,
marginTop: `27px`,
padding: `0 12px`,
borderRadius: `3px`,
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
fontSize: `14px`,
outline: `none`,
textOverflow: `ellipses`,
}}
/>
</SearchBox>
{props.isMarkerShown && (
<Marker position={fullPos} />
)}
</GoogleMap>
);
Where fullPos is the lat, lng variable I've been playing around with in different areas in hope it will work.
pulling the address external to the maps component
Not sure you mean Passing the new address from outside the component to it as a prop then, maybe you can use ComponentWillUpdate and make some beautiful conditions (to avoid bad endless loops) and also to check only if the passedAddressProp is changed, then maybe you can do something like: this.setState({center: passedAddressProp})
or
most likely obviously, you mean by it I get this address somewhere super far from this component so there's no chance they can communicate, then maybe the solution lies in redux/mobx (whichever you prefere/use) and when the address is changed, you can fire an action with the new address, and this component listens on this part of the state, also with some beautiful condition inside componentWillUpdate, you can change the this.state.center when this global part of the state changes.
Related
I am using an application that displays a Google map with react Google Maps have multiple pins installed, and the state changes by scrolling, and the active flight changes according to the state.
At that time, the center of the Google map is set to be an activity, but the Google map is re-rendered when the state changes. I don't know how to prevent rendering.
 Google Maps has NPM library. It uses react-google-maps and is implemented using hooks. I tried to return false with useEffect (), but I didn't hear it as it is. Please tell me
MapComponent(HOC)
import React from "react";
import { withGoogleMap, GoogleMap, withScriptjs, Marker, InfoWindow } from "react-google-maps";
import { compose, withProps, withHandlers, withStateHandlers } from "recompose";
const MapWithPlaces = compose(
withProps({
googleMapURL:
`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_PLACE_API_KEY}&libraries=geometry,drawing,places`,
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: "400px", width: "100%" }} />,
mapElement: <div style={{ height: "100%" }} />
}),
withStateHandlers(
props => ({
infoWindows: props.places.map(p => {
return { isOpen: false };
}),
defaultCenter: { 'lat': props.lat, 'lng': props.lng }
}),
{
onToggleOpen: ({ infoWindows }) => selectedIndex => ({
infoWindows: infoWindows.map((iw, i) => {
iw.isOpen = selectedIndex === i;
return iw;
})
})
}
),
withHandlers(() => {
const refs = {
map: undefined,
}
console.log(refs);
return {
onMapMounted: () => ref => {
refs.map = ref
},
onZoomChanged: ({ onZoomChange }) => (props) => {
const center = { 'lat': parseFloat(props.lat, 10), 'lng': parseFloat(props.lng, 10) }
refs.map.pantTo(center)
}
}
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap defaultZoom={props.zoom} defaultCenter={props.center} key={props.key} ref={map}>
{props.places &&
props.places.map((place, i) => {
let lat = parseFloat(place.lat, 10);
let lng = parseFloat(place.lng, 10);
return (
<Marker
id={place.id}
key={place.key}
position={{ lat: lat, lng: lng }}
title={place.name}
onClick={props.onToggleOpen.bind(this, i)}
opacity={place.key === props.step ? 1 : 0.5}
label={place.day === props.currentDay ? place.dayIndex.toString() : ''}
>
{props.infoWindows[i].isOpen && (
<InfoWindow onCloseClick={props.onToggleOpen.bind(i)}>
<div>{place.name}</div>
</InfoWindow>
)}
</Marker>
);
})}
</GoogleMap>
));
export default MapWithPlaces;
MapComponent(hooks)
import React, { useState, useEffect, useRef } from "react";
import { withGoogleMap, withScriptjs, GoogleMap, Marker, InfoWindow } from "react-google-maps";
// import mapStyles from "./mapStyles";
const MapCreate = React.memo((props) => {
// const [selectedPark, setSelectedPark] = useState(null);
const mapRef = useRef()
useEffect(() => {
console.log("props updates")
console.log(props);
const mapCenter = {
lat: parseFloat(props.places[props.step].lat, 10),
lng: parseFloat(props.places[props.step].lng, 10)
}
return false
// refMap.current.panTo(mapCenter) //move the map to new location
}, [props]);
return (
<GoogleMap defaultZoom={14} center={{ lat: props.center.lat, lng: props.center.lng }} ref={mapRef}>
{props.places && props.places.map((place, i) => {
let lat = parseFloat(place.lat, 10);
let lng = parseFloat(place.lng, 10);
return (
<Marker
id={place.id}
key={place.key}
position={{ lat: lat, lng: lng }}
title={place.name}
opacity={place.key === props.step ? 1 : 0.5}
label={place.day === props.currentDay ? place.dayIndex.toString() : ''}
>
</Marker>
)
})}
</GoogleMap>
)
})
const MapWrapped = withScriptjs(withGoogleMap(MapCreate));
export default function Map(props) {
const mapRef = useRef(null)
return (
<div style={{ width: "100%", height: "400px" }}>
<MapWrapped
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=${process.env.REACT_APP_GOOGLE_PLACE_API_KEY}`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: "400px", width: "100%" }} />}
mapElement={<div style={{ height: `100%` }} />}
{...props}
/>
</div>
);
}
Try #shouldcomponentupdate
shouldComponentUpdate(nextProps, nextState)
Use shouldComponentUpdate() to let React know if a component’s output
is not affected by the current change in state or props. The default
behavior is to re-render on every state change, and in the vast
majority of cases you should rely on the default behavior.
shouldComponentUpdate() is invoked before rendering when new props or
state are being received. Defaults to true. This method is not called
for the initial render or when forceUpdate() is used
I want to get the formatted_data out of two different SearchBoxes in the same GoogleMap in React.
I am using the react-google-maps library. I have no clue how to go about it and I haven't found any information about it on the entire internet.
I didn't forget to change my api key.
I have copied this code to my project from the documentation. But here is the code:
const _ = require("lodash");
const { compose, withProps, lifecycle } = require("recompose");
const {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
} = require("react-google-maps");
const { SearchBox } = require("react-google-maps/lib/components/places/SearchBox");
const MapWithASearchBox = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key={YOUR_API_KEY}&v=3.exp&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({
bounds: null,
center: {
lat: 41.9, lng: -87.624
},
markers: [],
onMapMounted: ref => {
refs.map = ref;
},
onBoundsChanged: () => {
this.setState({
bounds: refs.map.getBounds(),
center: refs.map.getCenter(),
})
},
onSearchBoxMounted: ref => {
refs.searchBox = ref;
},
onPlacesChanged: () => {
const places = refs.searchBox.getPlaces();
//this is the information that I need but I need it from the second SearchBox too
console.log(places[0].formatted_address);
const bounds = new window.google.maps.LatLngBounds();
places.forEach(place => {
if (place.geometry.viewport) {
bounds.union(place.geometry.viewport)
} else {
bounds.extend(place.geometry.location)
}
});
const nextMarkers = places.map(place => ({
position: place.geometry.location,
}));
const nextCenter = _.get(nextMarkers, '0.position', this.state.center);
this.setState({
center: nextCenter,
markers: nextMarkers,
});
// refs.map.fitBounds(bounds);
},
})
},
}),
withScriptjs,
withGoogleMap
)(props =>
<GoogleMap
ref={props.onMapMounted}
defaultZoom={15}
center={props.center}
onBoundsChanged={props.onBoundsChanged}
>
<SearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
controlPosition={window.google.maps.ControlPosition.TOP_LEFT}
onPlacesChanged={props.onPlacesChanged}
>
<input
type="text"
placeholder="Customized your placeholder"
style={{
boxSizing: `border-box`,
border: `1px solid transparent`,
width: `240px`,
height: `32px`,
marginTop: `27px`,
padding: `0 12px`,
borderRadius: `3px`,
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
fontSize: `14px`,
outline: `none`,
textOverflow: `ellipses`,
}}
/>
</SearchBox>
<SearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
controlPosition={window.google.maps.ControlPosition.TOP_LEFT}
onPlacesChanged={props.onPlacesChanged}
>
<input
type="text"
placeholder="Customized your placeholder"
style={{
boxSizing: `border-box`,
border: `1px solid transparent`,
width: `240px`,
height: `32px`,
marginTop: `27px`,
padding: `0 12px`,
borderRadius: `3px`,
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
fontSize: `14px`,
outline: `none`,
textOverflow: `ellipses`,
}}
/>
</SearchBox>
{props.markers.map((marker, index) =>
<Marker key={index} position={marker.position} />
)}
</GoogleMap>
);
return (
<MapWithASearchBox />
);
You are overriding the SearchBox reference here:
onSearchBoxMounted: ref => {
refs.searchBox = ref;
},
That's why you only see the second SearchBox. What you can do is create an array, like this:
onSearchBoxMounted: ref => {
if (!refs.searchBoxes || refs.searchBoxes.length === 0) {
refs.searchBoxes = [];
}
refs.searchBoxes.push(ref);
},
So that you can then do this:
onPlacesChanged: () => {
let places = [];
for (let i = 0; i < refs.searchBoxes.length; i++) {
places.push(refs.searchBoxes[i].getPlaces());
}
...
}
Now if you log places you'll see place information for each searchbox. I just tested this on my end and it works without problem so I hope this helps you!
I am currently trying to populate my google map with markers by using the map function. I can't seem to get anything to populate. Are there limitations that I am not understanding or am I missing something? I tried replacing FontAwesomeIcon with something more simple but it doesn't render. If you copy paste FontAwesomeIcon multiple times within the GoogleMapReact component it seems to work but I can't seem to make it work with map. Any suggestions would be much appreciated.
render() {
const {center, zoom} = this.props;
const listingPins = this.props.testList.map((listing, index) => {
console.log(listing);
if (listing.coordinates.lat === null || listing.coordinates.lng === null){
return null
} else{
return <FontAwesomeIcon icon={faHome} size={"2x"} key={index} listing={listing} lat={listing.coordinates.lat} lng={listing.coordinates.lat} />
}
});
console.log("TEST");
console.log(listingPins);
return (
<div style={{ height: '100vh', width: '100%' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: "key" }}
center={center}
zoom={zoom}
>
{listingPins}
</GoogleMapReact>
</div>
);
}
To show multiple markers on map, you have to pass an array of markers to the GoogleMapReact component as a child and map over it.
return (
<div style={{ height: '100vh', width: '100%' }}>
<GoogleMapReact>
{props.listingPins.map(pin => (
<Marker
position={{ lat: pin.latitude, lng: pin.longitude }}
key={pin.id}
/>
))}
</GoogleMapReact>
</div>
);
const createMarker = ({ map, maps }: Mapprops) => {
const markers = props.listingPins.map(data => {
return new maps.Marker({ position: data });
});
};
<GoogleMapReact
bootstrapURLKeys={{ key: "key" }}
center={center}
zoom={zoom}
onGoogleApiLoaded={createMarker}
>
</GoogleMapReact>
This would create the markers for you.
You need to make sure that the data object is in following format :
data: {
lat: number
lng: number
}
I'm facing the same problem.
I followed the answers above but they don't show any markers.
import React from 'react';
import GoogleMapReact, { Marker } from 'google-map-react';
let listingPins=[
{
id: 1,
latitude: 34.052235,
longitude: -118.243683,
shelter: 'Los Angeles'
},
{
id: 2,
latitude: 36.114647,
longitude: -115.172813,
shelter: 'Las Vegas'
},
{
id: 3,
latitude: 33.753746,
longitude: -84.386330,
shelter: 'Atlanta GA'
}
]
const defaultProps = {
center: {
lat: 36.114647,
lng: -115.172813
},
zoom: 10
};
const createMarker = ({ map, maps }: Mapprops) => {
const markers = listingPins.map(data => {
return new maps.Marker({ position: {lat: data.latitude, lng: data.longitude} });
});
};
export default function MainMap() {
return (
<GoogleMapReact
bootstrapURLKeys={{ key: "" }}
center={defaultProps.center}
zoom={defaultProps.zoom}
onGoogleApiLoaded={createMarker}
>
</GoogleMapReact>
)
}
I have some issue with Location Button I used react native maps Airbnb in my App,
when the first time I open the app and a map is rendered the button is disappeared but when I close the app * still in background * and reopen them the button appeared well like this GIF,
Link: https://imgur.com/37HF6H5
Note
I have seen all the issues same in the main repo of react native maps
but it's not working!
And other Q,
The App didn't ask me to open the GPS for the first time, just work when I opened manually
I have Android 8 * real device *
Here is my code
import React, { Component } from 'react';
import MapView, { Marker } from 'react-native-maps';
import { View, Text, StyleSheet, Dimensions } from 'react-native';
let { width, height } = Dimensions.get('window');
const LATITUDE = 31.78825;
const LONGITUDE = 34.4324;
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = 0.0421;
class Map extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
width: width,
marginBottom: 1,
region: {
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}
};
}
_findMe = async () => {
this.watchID = await navigator.geolocation.watchPosition(
({ coords }) => {
const { latitude, longitude } = coords
this.setState({
region: {
latitude,
longitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}
})
});
await navigator.geolocation.getCurrentPosition(
(position) => {
this.setState({
region: {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}
})
},
(error) => console.log(JSON.stringify(error)),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
)
}
componentDidMount() {
this._findMe();
}
componentWillUnmount() {
navigator.geolocation.clearWatch(this.watchId);
}
render() {
const { region } = this.state;
return (
<View style={styles.container}>
<MapView
style={[styles.map, { width: this.state.width }]}
style={StyleSheet.absoluteFill}
onMapReady={() => console.log(this.state.region)}
showsUserLocation
followsUserLocation={true}
region={region}
showsMyLocationButton={true}
// style={StyleSheet.absoluteFill}
textStyle={{ color: '#bc8b00' }}
containerStyle={{ backgroundColor: 'white', borderColor: '#BC8B00' }}
>
<Marker
coordinate={this.state.region}
title="Hello"
description="description"
/>
</MapView>
{/* <Text>{this.state.region.latitude}</Text> */}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
padding: 30,
flex: 1,
alignItems: 'center'
},
map: {
position: 'absolute',
zIndex: -1,
top: 0,
left: 0,
right: 0,
bottom: 0,
},
});
export default Map;
I know this is an old question but I wanted you share the solution to this bug on android platform.
There are two ways to solve this:
Make custom button, and then onPress using animateToRegion go to users location.
The reason, you are seeing the button after reopening the app is due to repaint/rerender (which solves this issue). So in short if you cause a rerender the button will appear.
snippet to get the idea
constructor(props) {
super(props);
this.state = {
bottomMargin: 1,
};
}
<MapView
showsUserLocation
style={{
marginBottom: this.state.bottomMargin, // using state in styling
...StyleSheet.absoluteFillObject,
}}
region={this.state.region}
onRegionChangeComplete={this.onRegionChange}
onMapReady={() => this.setState({ bottomMargin: 0 })} // this will fire once onReady
/>
I am working with google maps in reactjs, i need to pass the reference of the google map object, in order to send it in a function and further apply the functions associated with it. Basically i want to use the getCenter() method on the map object.. Refer this link: https://tomchentw.github.io/react-google-maps/
This is my map component from which i want the reference of my mapObject.
const MyMapComponent = compose(
withProps({
googleMapURL:
"https://maps.googleapis.com/maps/api/js?key=iDl-
mPD1j0K6lTEiMhs3D8axW53U&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div id="mapid" style={{ height: `100%` }} />
}),
lifecycle({
componentWillMount() {
console.log("Method called");
const refs = {}
this.setState({
bounds: null,
center: {
lat: 0, lng: 0
},
})
},
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap defaultZoom={15} defaultCenter={{ lat: props.lat, lng: props.lng
}} onCenterChanged={props.onChange(googlemapobjectref)}>
{props.isMarkerShown && (
<Marker position={{ lat: props.lat, lng: props.lng }} />
)}
</GoogleMap>
));
And in the App component (Parent Component) I want to use reference of the map object in the onChange() function.
class App extends Component {
constructor(props){
super(props);
this.state=
{
....
};
this.onChange = this.onChange.bind(this);
}
onChange(googlemapobjectref){
const val = googlemapobjectref.getCenter();
}
I just want to use getCenter() method in this code. If i am wrong at some point plz mention and if anything is not clear, mention that too. I will try to make it clear. Thanx in advance