How to use google map services with dynamically updated google api key? - reactjs

I'm developing an admin panel for controlling some of the users with REACT. So in that, I'm using react-google-maps third-party module to enabling google map services with dynamically changing google API key by admin. Here, after changing API key by admin, google map API link not gets updated with new values still, it keeps the old values until I reload the page.
import {
withScriptjs,
withGoogleMap,
GoogleMap,
LoadScript,
withProps,
Circle,
Marker
} from "react-google-maps";
import Geocode from "react-geocode";
var locationUpdate = document.getElementsByClassName('location');
const CustomSkinMap = withScriptjs(withGoogleMap(props => (
<GoogleMap
ref={props.onMapLoad}
center={{ lat: props.center.lat, lng: props.center.lng }}
defaultZoom={13}
defaultCenter={{ lat:props.lat, lng: props.lng }}
onDragEnd={props.onDragEnd}
onBoundsChanged={props.onBoundsChanged}
defaultOptions={{
scrollwheel: true,
disableDefaultUI: true,
defaultVisible: true,
zoomControl: true
}
}
/>
)
)
);
class LocationDetails extends React.Component {
constructor(props) {
super(props);
...
render() {
const { classes } = this.props;
const { lat, lng, bounds, center } = this.state;
var url = `https://maps.googleapis.com/maps/api/js?key=${this.state.googleApi}&libraries=places`
const google = window.google;
console.log(google, this.props);
console.log('render', this.state.googleApi);
return (
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<Card>
<FormHelperText error={!!this.state.errors}>
{this.state.errors + (this.state.errors ? ' *' : '')}
</FormHelperText>
<CardBody>
<CustomSkinMap
onBoundsChanged={this.onBoundsChanged}
onMapLoad={this.handleMapLoad}
onDragEnd={this.handleMapDrag}
onSearchBoxMounted={this.handleSearchBoxMounted}
bounds={bounds}
onPlacesChanged={this.handlePlacesChanged}
onCenterChanged={this.onCenterChanged}
center={center}
lat={lat}
lng={lng}
googleMapURL={url}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={
<div
style={{
height: `280px`,
borderRadius: "6px",
overflow: "hidden"
}}
/>
}
mapElement={<div style={{ height: `100%` }} />}
/>
<img
style={{
position: 'absolute',
top: '50%',
left: '50%'
}}
src={marker} alt="..." />
</CardBody>
</Card>
</GridItem>
</GridContainer>
);
}
}
Here customSkinmap with props passed to GoogleMap from react-google-maps

Related

Required props loadingElement or googleMapURL is missing. You need to provide both of them

I have this props but still get the same error. Required props loadingElement or googleMapURL is missing. You need to provide both of them.
import React from 'react';
import { GoogleMap,
withScriptjs,
withGoogleMap } from 'react-google-maps'
function Mapp () {
return (
<GoogleMap
defaultZoom = { 10 }
defaultCenter = {{ lat: 45.421532, lng: -75.697189 }}
/>
)
}
const WrapperMap = withScriptjs( withGoogleMap(Mapp))
export default function Maps() {
return (
<div style = {{ width: "100vw", height: "100vh" }}>
<WrapperMap
googleMapUrl = {`https://maps.googleapis.com/maps/api/js?
v=3.exp&libraries=geometry,
drawing,places&key=YOUR_KEY`}
loadingElement = { <div style={{ height: `100%` }} />}
containerElement = { <div style={{ height: `400px` }} />}
mapElement = { <div style={{ height: `100%` }} />}
/>
</div>
)
}
The issue with your code is that you are using the parameter googleMapUrl instead of googleMapURL. Once you change this, the code will successfully run. I also removed your API key. Please don't share your API key in public sites like StackOverflow in the future.
Here's a working code.

How to pass data from a parent component or use axios here in the map function for react-google-maps library?

I try to pass an array of the markers' coordinates and some other props from the index page (parent of the CardTransactionMapRGMs component) to the map function, so it can use the data to map the Markers. However, I can get the props in the CardTransactionMapRGMs, but I don't know how to get it in the Map function or how to reorganize it.
Also I'm quite confused about this "const WrapperMap = withScriptjs(withGoogleMap(Map));"
Thank you so much! Here's my code:
in index.js
<CardTransactionMapRGMs charges={this.state.charges} coordinates={this.state.coordinates} />
in CardTransactionMapRGMs.js
import React, { Component } from 'react';
import axios from 'axios';
import { GoogleMap, Marker, withScriptjs, withGoogleMap } from "react-google-maps";
import Constants from '../Constants';
import MapMarker from './MapMarker';
function Map() {
return (
<GoogleMap
defaultZoom={10}
defaultCenter={{ lat: 38.96, lng: -77.29 }}
>
//I want to use {props.coordinates.map(e)=> {
// <Marker...>}}here
<Marker position={{ lat: 38.96, lng: -77.29 }} />
</GoogleMap>
)
}
class CardTransactionMapRGMs extends Component {
render() {
const WrapperMap = withScriptjs(withGoogleMap(Map));
return (
<div>
<WrapperMap
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=${Constants.GOOGLE_MAP_API_KEY}`}
loadingElement={<div style={{ height: '100%' }}></div>}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
}
export default CardTransactionMapRGMs;
You can just use your props in your Map functional component.
function Map(props){
return <GoogleMap
defaultZoom={10}
defaultCenter={{ lat: 38.96, lng: -77.29 }}>
{props.coordinates.map(markerProps)=> <Marker {markerProps}>}
<Marker position={{ lat: 38.96, lng: -77.29 }} />
</GoogleMap>
}
CardTransactionMapRGMs.js
class CardTransactionMapRGMs extends Component {
render() {
const WrapperMap = withScriptjs(withGoogleMap(Map));
return (
<div>
<WrapperMap
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=${Constants.GOOGLE_MAP_API_KEY}`}
loadingElement={<div style={{ height: '100%' }}></div>}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
coordinates={[{ lat: 38.96, lng: -77.29 }]}
/>
</div>
);
}
}

Trigger Marker on external element hover without map re-rendering React

I have an airbnb-like app with cards on the left and a google map on the right (using the react-google-maps package)
I would like to highlight (in the code below through an animation) a marker when the user hovers on its corresponding card.
I actually managed to do so (see code below) but the problem is, the map component rerenders when another card is hovered on by the user.
Is there a way to do so without having the map to rerender everytime ?
My App.js (simplyfied for comprehension purposes):
import React from "react";
import { Meals } from "../api/meals.js";
import { Restaurants } from "../api/restaurants.js";
import MealCard from "./MealCard";
import MealsMap from "./MealsMap";
class App extends React.Component {
constructor() {
super();
this.state = {
highlightedMarker: ""
};
this.renderMeals = this.renderMeals.bind(this);
this.highlightMarker = this.highlightMarker.bind(this);
}
renderMeals() {
return this.props.meals.map(m => (
<div
className="col-sm-6 col-xs-12 "
key={m._id}
onMouseOver={() => this.highlightMarker(m.restaurant)}
>
<MealCard
name={m.name}
restaurant={
this.props.restaurants.find(r => r._id === m.restaurant).name
}
image={m.image}
address={
this.props.restaurants.find(r => r._id === m.restaurant).address
}
/>
</div>
));
}
renderMap() {
return (
<MealsMap
restaurants={this.props.restaurants}
highlightedMarker={this.state.highlightedMarker}
/>
);
}
highlightMarker(restaurantId) {
this.setState({ highlightedMarker: restaurantId });
}
render() {
return (
<div>
<div className="app-wrapper" style={{ display: "flex" }}>
<div className="container">
<div className="row">{this.renderMeals()}</div>
</div>
{this.renderMap()}
</div>
</div>
);
}
}
and my MealsMap.js:
import React from "react";
import { withGoogleMap, GoogleMap, Marker } from "react-google-maps";
class MealsMap extends React.Component {
render() {
const GoogleMapMeals = withGoogleMap(props => (
<GoogleMap
defaultCenter={{ lat: 50.6320134, lng: 3.0568584 }}
defaultZoom={13}
>
{this.props.restaurants.map(r => (
<Marker
key={r._id}
position={{ lat: Number(r.lat), lng: Number(r.lng) }}
animation={
this.props.highlightedMarker === r._id
? google.maps.Animation.BOUNCE
: ""
}
/>
))}
</GoogleMap>
));
return (
<GoogleMapMeals
containerElement={
<div
style={{
flex: "0 0 400px",
height: "100vh",
position: "sticky",
top: "0"
}}
/>
}
mapElement={
<div
style={{
height: "100%",
width: "100%",
position: "absolute",
top: "0px",
left: "0px",
backgroundColor: "rgb(229, 227, 223)"
}}
/>
}
/>
);
}
}
export default MealsMap;
You don't want to define the GoogleMapMeals component inside of the render method of MealsMap, since that will result in a new component each render which will make React unmount the previous one and create an entirely new one.
You could define GoogleMapMeals outside of the render method instead.
Example
const GoogleMapMeals = withGoogleMap(props => (
<GoogleMap
defaultCenter={{ lat: 50.6320134, lng: 3.0568584 }}
defaultZoom={13}
>
{props.markers.map(r => (
<Marker
key={r._id}
position={{ lat: Number(r.lat), lng: Number(r.lng) }}
animation={
props.highlightedMarker === r._id
? google.maps.Animation.BOUNCE
: ""
}
/>
))}
</GoogleMap>
));
class MealsMap extends React.Component {
render() {
return (
<GoogleMapMeals
markers={this.props.restaurants}
highlightedMarker={this.props.highlightedMarker}
containerElement={
<div
style={{
flex: "0 0 400px",
height: "100vh",
position: "sticky",
top: "0"
}}
/>
}
mapElement={
<div
style={{
height: "100%",
width: "100%",
position: "absolute",
top: "0px",
left: "0px",
backgroundColor: "rgb(229, 227, 223)"
}}
/>
}
/>
);
}
}

geolocation in React, use react-google-maps

How to change defaultCenter in react-google-maps ?
I need to find my geolocation and change default values lat and lng.
import React from "react";
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps";
const getLocation = () =>{
const pos = {};
const geolocation = navigator.geolocation;
if (geolocation) {
geolocation.getCurrentPosition(findLocal, showEror);
}
function findLocal(position){
pos.lat = position.coords.latitude;
pos.lng = position.coords.longitude;
}
function showEror(){console.log(Error)}
return pos;
};
const myLocation = getLocation(); // object has lat and lng
I need to transfer my data to the component MapWithAMarker next: Сenter={myLocation} and Marker position={myLocation}
class GoogleMapCenter extends React.Component{
render(){
const MapWithAMarker = withScriptjs(withGoogleMap(props =>
<GoogleMap
defaultZoom={10}
defaultCenter={{ lat: -34.397, lng: 150.644 }}>
{props.isMarkerShown && <Marker position={{ lat: -34.397, lng: 150.644 }} />}
</GoogleMap>
));
return(
<MapWithAMarker
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg&v=3.exp"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
)
}
}
export default GoogleMapCenter;
If I use this.props, it does not work.
<MapWithAMarker
center={this.props.myLocation}
position={this.props.myLocation}
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg&v=3.exp"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
or
<GoogleMap
defaultZoom={10}
defaultCenter={this.props.myLocation}>
{props.isMarkerShown && <Marker position={this.props.myLocation} />}
</GoogleMap>
Default properties like defaultCenter could only be set as initial state, center property could be used instead to re-render (center) the map.
The following example demonstrates how to center the map based on current location
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
currentLatLng: {
lat: 0,
lng: 0
},
isMarkerShown: false
}
}
showCurrentLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
position => {
this.setState(prevState => ({
currentLatLng: {
...prevState.currentLatLng,
lat: position.coords.latitude,
lng: position.coords.longitude
},
isMarkerShown: true
}))
}
)
} else {
error => console.log(error)
}
}
componentDidMount() {
this.showCurrentLocation()
}
render() {
return (
<div>
<MapWithAMarker
isMarkerShown={this.state.isMarkerShown}
currentLocation={this.state.currentLatLng} />
</div>
);
}
}
where
const MapWithAMarker = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withScriptjs,
withGoogleMap
)((props) =>
<GoogleMap
defaultZoom={8}
center={{ lat: props.currentLocation.lat, lng: props.currentLocation.lng }}
>
{props.isMarkerShown && <Marker position={{ lat: props.currentLocation.lat, lng: props.currentLocation.lng }} onClick={props.onMarkerClick} />}
</GoogleMap>
)
Demo (edit)

react-google-maps : Can't get bounds

I have the following react component that loads a GoogleMap from 'react-google-maps' library. I'm following the documentation examples but I get undefined from getBounds() function. I think it is due to trying to get the bounds before map is fully loaded but can't find a solution.
#inject("map") #observer
export default class Map extends Component {
constructor(props) {
super(props);
}
render() {
const mapStore = this.props.map
const GettingStartedGoogleMap = withGoogleMap(props => (
<GoogleMap
ref={props.onMapLoad}
style={{
height: 100,
width: 100,
}}
defaultOptions={{ styles: this.mapStyles }}
defaultZoom={mapStore.zoom}
defaultCenter={{ lat: mapStore.center.lat, lng: mapStore.center.lng }}>
{
mapStore.markers.map(({ lat, lng }) => {
return <Marker
position={{ lat, lng }}
icon={{
url: require('../../images/marker.png')
}}
/>
})
}
</GoogleMap>
))
return (
<GettingStartedGoogleMap
style={{
height: 100,
width: 100,
}}
onMapLoad={(googleMap) => {
console.log(googleMap.getBounds())
}}
containerElement={
<div style={{ height: 'calc(100vh - 70px)' }
} />
}
mapElement={
<div style={{ height: 'calc(100vh - 70px)' }} />
} />
)
}
}
Thanks in advance
you can use 'onIdle' to make sure map is fully ready
<GoogleMap
ref={props.onMapLoad}
...
onIdle={props.onMapIdle}
>
...
</GoogleMap>
and
<GettingStartedGoogleMap
onMapIdle={ ()=> { console.log('map is ready') } }
...
/>

Resources