react-google-maps resets map when using onClick events - reactjs

I'm having difficulty implementing some simple react-google-maps code where whenever a marker is clicked or an info box is closed, the map will reset its position to the original position. I saw that I should be rendering the map component separately from changing the state, but I'm not sure how to implement this.
Any help is appreciated
import React, {useState} from 'react';
import {Marker, GoogleMap,LoadScript,InfoWindow} from '#react-google-maps/api';
const MapContainer = ({markers}) => {
const [selected, setSelected] = useState<any>({})
const onSelect = (item) => {
setSelected(item)
}
const mapStyles = {
height: "100vh",
width: "100%",
}
const defaultCenter = {
lat: 45.510440, lng: -122.683338
}
const renderMap = () => {
return <>
<div>
<LoadScript
googleMapsApiKey='[GOOGLE API KEY]'>
<GoogleMap
mapContainerStyle={mapStyles}
zoom={13}
center={defaultCenter}
>
{markers.map((marker)=> {
return(
<Marker
position={{
lat: marker.lat,
lng: marker.lng
}}
title={marker.title}
onClick={ () => onSelect(marker) }
/>
)})
}
{
selected.lng &&
<InfoWindow
position={
{
lat: selected.lat,
lng: selected.lng
}
}
onCloseClick={() => setSelected({})}
>
<div>
<h3>{selected.title}</h3>
<p>{selected.info}</p>
</div>
</InfoWindow>
}
</GoogleMap>
</LoadScript>
</div>
</>
}
return renderMap()
}
export default MapContainer; ```

It seems that putting your center value in a state and setting the clicked marker coordinates as the new center will solve the issue. Here's a sample code and a code snippet:
import React, {useState} from 'react';
import {Marker, GoogleMap,LoadScript,InfoWindow} from '#react-google-maps/api';
import { render } from "react-dom";
import markers from "./data.json"
const MapContainer = () => {
const [selected, setSelected] = useState<any>({})
const [center,setCenter]= useState({
lat: 39.952584, lng: -75.165221
})
const onSelect = (item) => {
setSelected(item)
setCenter({
lat: item.lat, lng: item.lng
})
}
const mapStyles = {
height: "100vh",
width: "100%",
}
const renderMap = () => {
return <>
<div>
<LoadScript
googleMapsApiKey='AIzaSyBlfuRgAUDPGJnUpwyyhdSBIs193bXboMQ'>
<GoogleMap
mapContainerStyle={mapStyles}
zoom={13}
center={center}
>
{markers.map((marker,i)=> {
return(
<Marker
key={i}
position={{
lat: marker.lat,
lng: marker.lng
}}
title={marker.title}
onClick={ () => onSelect(marker) }
/>
)})
}
{
selected.lng &&
<InfoWindow
position={
{
lat: selected.lat,
lng: selected.lng
}
}
onCloseClick={() => setSelected({})}
>
<div>
<h3>{selected.id}</h3>
<p>{selected.name}</p>
</div>
</InfoWindow>
}
</GoogleMap>
</LoadScript>
</div>
</>
}
return renderMap()
}
render(<MapContainer />, document.getElementById('root'));

Put the center in a state and set it to null after tiles are loaded, like this:
import React, {useState} from 'react';
import {Marker, GoogleMap,LoadScript,InfoWindow} from '#react-google-maps/api';
const MapContainer = ({markers}) => {
const [selected, setSelected] = useState<any>({})
const onSelect = (item) => {
setSelected(item)
}
const mapStyles = {
height: "100vh",
width: "100%",
}
const defaultCenter = {
lat: 45.510440, lng: -122.683338
}
const [center, setCenter] = useState(defaultCenter)
const renderMap = () => {
return <>
<div>
<LoadScript
googleMapsApiKey='[GOOGLE API KEY]'>
<GoogleMap
mapContainerStyle={mapStyles}
zoom={13}
center={center}
onTilesLoaded={() => setCenter(null)}
>
{markers.map((marker)=> {
return(
<Marker
position={{
lat: marker.lat,
lng: marker.lng
}}
title={marker.title}
onClick={ () => onSelect(marker) }
/>
)})
}
{
selected.lng &&
<InfoWindow
position={
{
lat: selected.lat,
lng: selected.lng
}
}
onCloseClick={() => setSelected({})}
>
<div>
<h3>{selected.title}</h3>
<p>{selected.info}</p>
</div>
</InfoWindow>
}
</GoogleMap>
</LoadScript>
</div>
</>
}
return renderMap()
}
export default MapContainer;

Related

How to show in a list, visible markers in the google map with React

I want to show in a list next to the map, the restaurant markers visible in the google map.
This exemple is a good illustration: http://jsfiddle.net/glafarge/mbuLw/
I use #react-google-maps/api with React Functional Component.
The restaurants are contained in a JSON file.
I tried to get bounds with this code:
import React, {useState, useEffect} from "react";
import {GoogleMap, LoadScript, Marker, InfoWindow} from '#react-google-maps/api';
const Map = (props) => {
const [currentPosition, setCurrentPosition] = useState({});
const [markerObjects, setMarkerObjects] = useState([]);
const success = position => {
const currentPosition = {
lat: position.coords.latitude,
lng: position.coords.longitude
}
setCurrentPosition(currentPosition);
};
// useEffect(() => {
navigator.geolocation.getCurrentPosition(success);
// })
const containerStyle = {
width: '400px',
height: '400px'
};
return (
<LoadScript
googleMapsApiKey="AIzaSyC2-n39eQnutXECIDc-9tlNMNFmxzshDtE"
>
<GoogleMap
mapContainerStyle={containerStyle}
center={currentPosition}
zoom={10}
onDragEnd={()=> {
const bounds = new window.google.maps.LatLngBounds();
console.log(bounds);
}}
>
{props.restaurantsJson.map(
(element, index) => <Marker
key={index}
// ref={onMarkerMounted}
position=
{
{
lat: element.lat,
lng: element.long
}
}
icon={{url: "https://maps.google.com/mapfiles/ms/icons/blue-dot.png"}}
name={element.restaurantName}
/>
)}
<Marker
position={currentPosition}
>
<InfoWindow
options=
{
{
pixelOffset:
{
width: 0,
height: -45
}
}
}
position={currentPosition}>
<div>
<p>Your current position</p>
</div>
</InfoWindow>
</Marker>
</GoogleMap>
</LoadScript>
);
};
export default Map;
But i get this result:
What is the 180 -180, 1 -1 result ?
in comparison with GPS coordinates of a restaurant (45.918057356570735, 6.1445196863029645)
The goal is to reproduce this type of condition:
if (
$scope.map.bounds.southwest.latitude < marker.coords.latitude &&
marker.coords.latitude < $scope.map.bounds.northeast.latitude &&
$scope.map.bounds.southwest.longitude < marker.coords.longitude &&
marker.coords.longitude < $scope.map.bounds.northeast.longitude
) {
visibleMarkers.push(marker);
}
And refresh the list with only visible markers in the map.
Hope you help me, thanks.
I get bounds with this code:
import React from "react";
import { GoogleMap, useJsApiLoader } from "#react-google-maps/api";
const containerStyle = {
width: "400px",
height: "400px"
};
const center = {
lat: 45.745,
lng: 5.65
};
function Map() {
const { isLoaded } = useJsApiLoader({
id: "google-map-script",
googleMapsApiKey: "AIzaSyC2-n39eQnutXECIDc-9tlNMNFmxzshDtE"
});
const [map, setMap] = React.useState(null);
const onLoad = React.useCallback(function callback(map) {
const bounds = new window.google.maps.LatLngBounds();
console.log(bounds);
// map.fitBounds(bounds);
setMap(map);
}, []);
const onUnmount = React.useCallback(function callback(map) {
setMap(null);
}, []);
return isLoaded ? (
<GoogleMap
mapContainerStyle={containerStyle}
center={center}
zoom={10}
onLoad={onLoad}
onUnmount={onUnmount}
onDragEnd={() => console.log(map.getBounds())}
>
{/* Child components, such as markers, info windows, etc. */}
<></>
</GoogleMap>
) : (
<></>
);
}
export default React.memo(Map);

React: Export function to use in another component

I'm building out a Google Map on my Gatsby site with a search box that will allow users to search for their location. I've got a Hero component and a Map component, and all of the functionality is built into the Map component; the Google Maps API, autocomplete, Google Places etc. Here's what it looks like right now:
Map.tsx
import React, { useState, useRef, useCallback } from 'react';
import { GoogleMap, useLoadScript, Marker, InfoWindow } from '#react-google-maps/api';
import * as dealersData from 'assets/data/dealers.json';
import { Button } from 'components/button/Button';
import MapMarker from 'assets/images/icons/map-marker.png';
import SearchIcon from 'assets/svg/search.svg';
import usePlacesAutocomplete, { getGeocode, getLatLng } from 'use-places-autocomplete';
import {
Combobox,
ComboboxInput,
ComboboxPopover,
ComboboxList,
ComboboxOption,
} from '#reach/combobox';
import '#reach/combobox/styles.css';
import s from './Map.scss';
import MapStyles from './_MapStyles';
const libraries = ['places'];
const mapContainerStyle = {
width: '100%',
height: '100%',
};
const options = {
styles: MapStyles,
disableDefaultUI: true,
zoomControl: true,
};
interface MapProps {
location: any;
fetchedData: ReactNode;
}
export const Map = ({ location, fetchedData }: MapProps) => {
const center = {
lat: location.location.latitude,
lng: location.location.longitude,
};
const [selectedDealer, setselectedDealer] = useState(null);
const { isLoaded, loadError } = useLoadScript({
googleMapsApiKey: process.env.GATSBY_GOOGLE_MAPS_API,
libraries,
});
const mapRef = useRef();
const onMapLoad = useCallback((map) => {
mapRef.current = map;
}, []);
const panTo = useCallback(({ lat, lng }) => {
mapRef.current.panTo({ lat, lng });
mapRef.current.setZoom(10);
}, []);
if (loadError) return <div className={s.map}>Error loading maps...</div>;
if (!isLoaded) return <div className={s.map}>Loading...</div>;
return (
<>
<div className={s.map}>
<div className={s.map__search}>
<Search panTo={panTo}></Search>
</div>
<GoogleMap
mapContainerStyle={mapContainerStyle}
zoom={10}
center={center}
options={options}
onLoad={onMapLoad}
>
{dealersData.features.map((dealer) => (
<Marker
key={dealer.properties.DEALER_ID}
position={{
lat: dealer.geometry.coordinates[1],
lng: dealer.geometry.coordinates[0],
}}
onClick={() => {
setselectedDealer(dealer);
}}
icon={{
url: MapMarker,
scaledSize: new window.google.maps.Size(30, 30),
}}
/>
))}
{selectedDealer && (
<InfoWindow
position={{
lat: selectedDealer.geometry.coordinates[1],
lng: selectedDealer.geometry.coordinates[0],
}}
onCloseClick={() => {
setselectedDealer(null);
}}
>
<div className={s.dealer}>
<h2 className={s.dealer__name}>
{selectedDealer.properties.NAME}
<span className={s.phone}>{selectedDealer.properties.PHONE_NUMBER}</span>
</h2>
<div className={s.dealer__info}>
<p className={s.address}>{selectedDealer.properties.ADDRESS}</p>
<p className={s.address}>
{selectedDealer.properties.CITY}, {selectedDealer.properties.STATE}{' '}
{selectedDealer.properties.ZIP_CODE}
</p>
</div>
<Button>Set Location</Button>
</div>
</InfoWindow>
)}
</GoogleMap>
</div>
</>
);
};
export function Search({ panTo }) {
const {
ready,
value,
suggestions: { status, data },
setValue,
clearSuggestions,
} = usePlacesAutocomplete({
requestOptions: {
location: { lat: () => 38.8299359, lng: () => -121.3070356 },
radius: 200 * 1000,
},
});
return (
<>
<div className={s.search__input}>
<i className={s.search__icon}>
<SearchIcon />
</i>
<Combobox
onSelect={async (address) => {
setValue(address, false);
clearSuggestions();
try {
const results = await getGeocode({ address });
const { lat, lng } = await getLatLng(results[0]);
panTo({ lat, lng });
} catch (error) {
console.log('error');
}
}}
>
<ComboboxInput
value={value}
onChange={(e: any) => {
setValue(e.target.value);
}}
disabled={!ready}
placeholder="Enter your location"
/>
<ComboboxPopover>
<ComboboxList>
{status === 'OK' &&
data.map(({ id, description }) => <ComboboxOption key={id} value={description} />)}
</ComboboxList>
</ComboboxPopover>
</Combobox>
</div>
</>
);
}
And here's my Hero component:
Hero.tsx
import React from 'react';
import s from './Hero.scss';
import { RichText } from 'prismic-reactjs';
import { linkResolver } from 'utils/linkResolver';
import htmlSerializer from 'utils/htmlSerializer';
import { Search } from '../map/_Map';
export const Hero = ({ content, panTo }: any) => (
<div className={s.hero} data-theme={content.theme}>
<div className={s.hero__container}>
<div className={s.content}>
<h1 className={s.content__heading}>{RichText.asText(content.page_title)}</h1>
<div className={s.content__copy}>
{RichText.render(content.copy, linkResolver, htmlSerializer)}
</div>
<Search panTo={panTo}></Search>
</div>
</div>
</div>
);
Essentially, I'm needing to utilize the Search function in my Hero component, but when I export it and import it into the Hero its rendering just fine, but it never loads the use-places-autocomplete library, and won't work. What am I doing wrong? Is there any way to export the search function to reuse?
I've created an SSCCE, as asked in the comments here. The search function works if I utilize it directly in the Map component, but if it's imported into the hero, it doesn't load.
https://codesandbox.io/s/dawn-glitter-zi7lx
Thanks.
Thanks for providing the sscce of your code in codesandbox. It looks like you are loading the Google Maps Javascript script tag only inside the <Map/> and not inside <Hero/>. You can see in your console log that there is a message to load the library.
To make it work, I used LoadScript of #react-google-maps/api in your index.tsx and put both and components in order for both script to work. Here's a sample of the index.tsx.
import React from "react";
import ReactDOM from "react-dom";
import { LoadScript } from "#react-google-maps/api";
import { Map } from "./Map";
import { Hero } from "./Hero";
const rootElement = document.getElementById("root");
const App = () => {
return (
<React.StrictMode>
<LoadScript
googleMapsApiKey="YOUR_API_KEY"
libraries={["places"]}
>
<Hero />
<Map />
</LoadScript>
</React.StrictMode>
);
};
ReactDOM.render(<App />, rootElement);
I also removed useLoadScript in Map.tsx to make sure that it won't have conflict with the script loaded in the index.tsx.

Display Directions with React Google Maps

I am new to React and am attempting to use google maps to display directions. I have been able to get it to display a single marker but have not found how to reconfigure the code to display the directions. Below is my most recent attempt but it will only display the map... any assistance is appreciated:
import React, { Component } from 'react';
import { withGoogleMap, GoogleMap, DirectionsRenderer } from 'react-google-maps';
class Map extends Component {
render() {
const GoogleMapExample = withGoogleMap(props => (
<GoogleMap
defaultCenter = { { lat: 40.756795, lng: -73.954298 } }
defaultZoom = { 13 }
>
<DirectionsRenderer origin={{ lat: 40.756795, lng: -73.954298 }} destination={{ lat: 41.756795, lng: -78.954298 }} />
</GoogleMap>
));
return(
<div>
<GoogleMapExample
containerElement={ <div style={{ height: `500px`, width: '500px' }} /> }
mapElement={ <div style={{ height: `100%` }} /> }
/>
</div>
);
}
};
export default Map;
I have the API key in a script tag in index.html
DirectionsRenderer component does not accept origin and destination props, directions prop needs to be provided instead which value represents the response from DirectionsService, for example:
<DirectionsRenderer
directions={this.state.directions}
/>
where
const directionsService = new google.maps.DirectionsService();
const origin = { lat: 40.756795, lng: -73.954298 };
const destination = { lat: 41.756795, lng: -78.954298 };
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
Demo
This should be the enough example for you to work with
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { withScriptjs } from "react-google-maps";
import Map from './components/Map';
function App() {
const MapLoader = withScriptjs(Map);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
</header>
<MapLoader
googleMapURL="https://maps.googleapis.com/maps/api/js?key=Key"
loadingElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
export default App;
And your Map.js file should look like this
/*global google*/
import React, { Component } from "react";
import {
withGoogleMap,
withScriptjs,
GoogleMap,
DirectionsRenderer
} from "react-google-maps";
class Map extends Component {
state = {
directions: null,
};
componentDidMount() {
const directionsService = new google.maps.DirectionsService();
const origin = { lat: 6.5244, lng: 3.3792 };
const destination = { lat: 6.4667, lng: 3.4500};
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING,
waypoints: [
{
location: new google.maps.LatLng(6.4698, 3.5852)
},
{
location: new google.maps.LatLng(6.6018,3.3515)
}
]
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
console.log(result)
this.setState({
directions: result
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
}
render() {
const GoogleMapExample = withGoogleMap(props => (
<GoogleMap
defaultCenter={{ lat: 6.5244, lng: 3.3792 }}
defaultZoom={13}
>
<DirectionsRenderer
directions={this.state.directions}
/>
</GoogleMap>
));
return (
<div>
<GoogleMapExample
containerElement={<div style={{ height: `500px`, width: "500px" }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
}
export default Map;
I hope this helps you
Similar to #VadimGremyachev and #EmmanuelAdebayo's answers (many thanks!), but with an arrow function and a useState hook:
import React, { useState } from "react";
import { GoogleMap, Marker, DirectionsRenderer } from "react-google-maps";
/* global google */
const Map = ({ formattedOrigin, formattedDestination }) => {
const DirectionsService = new google.maps.DirectionsService();
let [directions, setDirections] = useState("");
DirectionsService.route(
{
origin: formattedOrigin,
destination: formattedDestination,
travelMode: google.maps.TravelMode.DRIVING,
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
setDirections(result);
} else {
console.error(`error fetching directions ${result}`);
}
}
);
return (
<section className="googleMap">
<GoogleMap defaultZoom={9} defaultCenter={{ lat: 41.75, lng: 1.8 }}>
<Marker position={formattedOrigin} />
<Marker position={formattedDestination} />
{directions && <DirectionsRenderer directions={directions} />}
</GoogleMap>
</section>
);
};
export default Map;
And then from your high order component:
import React from "react";
import "../styles/Home.css";
import { useSelector } from "react-redux";
import { googleMapsApiKey } from "../../data/constants";
import { withScriptjs, withGoogleMap } from "react-google-maps";
import Map from "../presentational/Map";
const Home = () => {
const WrappedMap = withScriptjs(withGoogleMap(Map));
const formattedOrigin = useSelector(
(state) => state.routeCalculatorReducer.loadCost?.originGeoCodedFormatted
);
const formattedDestination = useSelector(
(state) =>
state.routeCalculatorReducer.loadCost?.destinationGeoCodedFormatted
);
return (
<main className="home">
<section className="map">
<WrappedMap
googleMapURL={`https://maps.googleapis.com/maps/api/js?libraries=geometry,drawing,places&key=${googleMapsApiKey}`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: "80vh" }} />}
mapElement={<div style={{ height: `100%` }} />}
formattedOrigin={formattedOrigin}
formattedDestination={formattedDestination}
/>
</section>
</main>
);
};
export default Home;

Update Google Map based on Geolocation with React

I'm trying to show Google Map with centering the map based on latitude and longitude which are returned by Geolocation. However, the map shows as the default value and not get rendered by Geolocation values. I set latitude and longitude in component state and trying to re-render the component after the state is updated. But it does not work. Below is my code.
MapView.js
import React, { Component } from 'react'
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from 'react-google-maps'
import MapComponent from './MapComponent'
class MapView extends Component {
constructor(props){
super(props)
this.state = {
currentLatLng: {
lat: 0,
lng: 0
},
isMarkerShown: false
}
}
componentWillUpdate(){
this.getGeoLocation()
}
componentDidMount() {
this.delayedShowMarker()
}
delayedShowMarker = () => {
setTimeout(() => {
this.getGeoLocation()
this.setState({ isMarkerShown: true })
}, 5000)
}
handleMarkerClick = () => {
this.setState({ isMarkerShown: false })
this.delayedShowMarker()
}
getGeoLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
position => {
this.setState({
lat: position.coords.latitude,
lng: position.coords.longitude
})
}
)
} else {
error => console.log(error)
}
}
render() {
return (
<MapComponent
isMarkerShown={this.state.isMarkerShown}
onMarkerClick={this.handleMarkerClick}
currentLocation={this.state.currentLatLng}
/>
)
}
}
export default MapView;
MapComponent.js
import React, { Component } from 'react'
import { compose, withProps } from 'recompose'
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from 'react-google-maps'
const MapComponent = compose(
withProps({
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%` }} />,
}),
withScriptjs,
withGoogleMap
)((props) =>
<GoogleMap
defaultZoom={8}
defaultCenter={{ lat: props.currentLocation.lat, lng: props.currentLocation.lng }}
>
{props.isMarkerShown && <Marker position={{ lat: props.currentLocation.lat, lng: props.currentLocation.lng }} onClick={props.onMarkerClick} />}
</GoogleMap>
)
export default MapComponent
In fact map is not centered since currentLatLng is not getting updated, you might want something like this:
getGeoLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
position => {
console.log(position.coords);
this.setState(prevState => ({
currentLatLng: {
...prevState.currentLatLng,
lat: position.coords.latitude,
lng: position.coords.longitude
}
}))
}
)
} else {
error => console.log(error)
}
}
instead of original getGeoLocation function
According to w3.org, the current location can be obtained with:
function showMap(position) {
// Show a map centered at (position.coords.latitude, position.coords.longitude).
console.log(position.coords.latitude);
console.log(position.coords.longitude);
}
// One-shot position request.
navigator.geolocation.getCurrentPosition(showMap);
This links Geolocation API:
Geolocation API w3.org
This worked for me, location high accuracy.

React-google-maps infowindow `React.Children.only expected to receive a single React element child.`

I am trying to open a infowindow on a specific marker on click, however when i click one it appears that all of them are opening instead, and showing me this error:
React.Children.only expected to receive a single React element child.
This is what my code looks like right now:
import React, { Component } from 'react';
import { GoogleMap, Marker, withGoogleMap, withScriptjs, InfoWindow } from 'react-google-maps'
class Map extends Component {
state = {
isOpen: false
}
handleMarkerClick = () => {
this.setState({ isOpen: true})
}
render() {
return(
<div>
<GoogleMap
defaultZoom={13}
defaultCenter={{ lat: -22.9034778, lng: -43.1264636 }}
>{this.props.markers.map((marker, index) =>(
<Marker
key={index}
position={marker.location}
onClick={this.handleMarkerClick}
>{this.state.isOpen && <InfoWindow onCloseClick={this.handleMarkerClick}/>}
</Marker>
))}
</GoogleMap>
</div>
)
}
}
export default withScriptjs(withGoogleMap(Map))
Start of Edit
I made some changes to try and address the comment, however it isn't working yet, can you give me some hints on what i'm doing wrong, since i made some changes to the top component i will paste it here too:
import React, { Component } from 'react';
import Map from './Map.js'
import List from './List.js'
import escapeRegExp from 'escape-string-regexp'
import sortBy from 'sort-by'
class App extends Component {
state ={
locations:[
{
name: "Paróquia Nossa Senhora das Dores do Ingá",
location: {
lat: -22.9038875,
lng: -43.1252873
},
isOpen:false,
},
{
name: "Museu de Arte Contemporanea de Niteroi",
location: {
lat: -22.9078182,
lng: -43.1262919
},
isOpen:false,
},
{
name: "UFF - Faculdade de Direito",
location: {
lat: -22.9038469,
lng: -43.126024
},
isOpen:false,
},
{
name: "Ponte Rio-Niterói",
location: {
lat: -22.8701,
lng: -43.167
},
isOpen:false,
},
{
name: "Fundação Oscar Niemeyer",
location: {
lat: -22.888533927137285,
lng: -43.12815992250511
},
isOpen:false,
},
{
name: "Campo de São Bento",
location: {
lat: -22.905279,
lng: -43.107759
},
isOpen:false,
}
],
query:''
}
onToggleOpen = (location) => {
this.setState({ isOpen: !this.isOpen })
}
updateQuery = (query) => {
this.setState({ query: query.trim() })
console.log(query)
}
componentDidMount() {}
render() {
const { query, locations } = this.state
let filteredMarkers
if(query) {
const match = new RegExp(escapeRegExp(query), 'i')
filteredMarkers = locations.filter((location) => match.test(location.name))
}else {
filteredMarkers = locations
}
filteredMarkers.sort(sortBy('name'))
return (
<div className="App">
<div style={{height:`5vh`}}>
<input
type='text'
placeholder='Search locations'
value={query}
onChange={(event) => this.updateQuery(event.target.value)}
/>
</div>
<List
markers={filteredMarkers}
/>
<Map
onToggle={this.onToggleOpen}
googleMapURL="https://maps.googleapis.com/maps/api/js?&key=AIzaSyAiqO5W1p5FAFf8RZD11PGigUXSlmVHguQ&v=3"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `80vh` }} />}
mapElement={<div style={{ height: `100%` }} />}
className="Map"
markers={filteredMarkers}
/>
</div>
);
}
}
export default App;
Map.js
import React, { Component } from 'react';
import { GoogleMap, Marker, withGoogleMap, withScriptjs, InfoWindow } from 'react-google-maps'
class Map extends Component {
render() {
return(
<div>
<GoogleMap
defaultZoom={13}
defaultCenter={{ lat: -22.9034778, lng: -43.1264636 }}
>{this.props.markers.map((marker, index) =>(
<Marker
key={index}
position={marker.location}
onClick={() => this.props.onToggle(marker)}
>{marker.isOpen && <InfoWindow onCloseClick={this.ontoggleOpen}>Hello</InfoWindow>}
</Marker>
))}
</GoogleMap>
</div>
)
}
}
export default withScriptjs(withGoogleMap(Map))
The problem i was having with React.Children.only expected to receive a single React element child. was being caused because i didn't set a div inside the infowindow, so simply by adding it this particular problem was solved.
Here is what it used to look like:
<InfoWindow onCloseClick={this.handleMarkerClick}/>
here is what it should look like:
<InfoWindow onCloseClick={()=>this.props.onToggle(marker)}><div>Hello</div></InfoWindow>
or something along these lines.

Resources