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;
Related
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;
I try to display gmaps with a direction route between 2 points. So I built:
A container class:
import React, { Component } from 'react';
import { DirectionsRenderer } from 'react-google-maps';
import Map from './Map';
class MapContainer extends Component {
constructor(props) {
super(props);
this.state = { directions: null };
}
componentWillMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route(
{
origin: new google.maps.LatLng(41.85073, -87.65126),
destination: new google.maps.LatLng(41.85258, -87.65141),
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
}
render() {
return (
<Map
googleMapURL={`https://maps.googleapis.com/maps/api/js?key=<APIKEY>&v=3.exp&libraries=geometry,drawing,places`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `600px`, width: `100%` }} />}
mapElement={<div style={{ height: `100%` }} />}
directions={this.state.directions}
/>
);
}
}
export default MapContainer;
It detects the REACT lifecycle and fetches the JS code from GMaps API and then passes it down to the Map component:
import React, { Component } from 'react';
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
DirectionsRenderer
} from 'react-google-maps';
import package_ico from '../img/package.png';
const Map = withScriptjs(
withGoogleMap(props => {
const marker_package = (
<Marker
position={{
lat: 41.85073,
lng: -87.65126
}}
icon={package_ico}
/>
);
const marker_destination = (
<Marker
position={{
lat: 41.85258,
lng: -87.65141
}}
/>
);
if (props.directions != null) {
console.log('renderdir');
console.log(props.directions);
return (
<GoogleMap defaultZoom={14} center={{ lat: 41.85073, lng: -87.65126 }}>
{marker_package}
{marker_destination}
{props.directions && (
<DirectionsRenderer directions={props.directions} />
)}
</GoogleMap>
);
} else {
console.log('rendernodirec');
return (
<GoogleMap defaultZoom={14} center={{ lat: 41.85073, lng: -87.65126 }}>
{marker_package}
{marker_destination}
</GoogleMap>
);
}
})
);
export default Map;
The data passes correctly from the MapContainer down to the Map but then it seems like the DirectionsRenderer component that is suppose to manage the result doesn't ingest the data correctly and I get the below error message.
57 Uncaught Fc {message: "not a LatLngBounds or LatLngBoundsLiteral: unknown property f", name: "InvalidValueError", stack: "Error↵ at new Fc (https://maps.googleapis.com/m…3.exp&libraries=geometry,drawing,places:170:4324)"}
message: "not a LatLngBounds or LatLngBoundsLiteral: unknown property f"
name: "InvalidValueError"
What am I doing wrong?
I tried to follow the example: https://tomchentw.github.io/react-google-maps/#directionsrenderer but I would like to avoid using recompose since I found it quite confusing...
Thanks for your feedback.
I wasn't able to reproduce the same error, but it could be the sequence of loading the Google Maps script. Since you are using withScriptJs, your call to google.maps.* should be within the component that is wrapped by withScriptJs, where in your example it is outside. Try moving your componentWillMount function into the Map component as in the example below.
If this resolves the issue, it is a race condition caused by the Google Maps script not being loaded before componentWillMount is fired and google.maps.* would be unavailable.
I have a working example on CodeSandbox here. Most of the code is copied from your examples above. Just put in your API key.
import React, { Component } from "react";
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
DirectionsRenderer
} from "react-google-maps";
class Map extends React.Component {
constructor(props) {
super(props);
this.state = { directions: null };
}
componentWillMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route(
{
origin: new google.maps.LatLng(41.85073, -87.65126),
destination: new google.maps.LatLng(41.85258, -87.65141),
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
}
render() {
const marker_package = (
<Marker
position={{
lat: 41.85073,
lng: -87.65126
}}
/>
);
const marker_destination = (
<Marker
position={{
lat: 41.85258,
lng: -87.65141
}}
/>
);
if (this.state.directions != null) {
console.log("renderdir");
console.log(this.state.directions);
return (
<GoogleMap defaultZoom={14} center={{ lat: 41.85073, lng: -87.65126 }}>
{marker_package}
{marker_destination}
{this.state.directions && (
<DirectionsRenderer directions={this.state.directions} />
)}
</GoogleMap>
);
} else {
console.log("rendernodirec");
return (
<GoogleMap defaultZoom={14} center={{ lat: 41.85073, lng: -87.65126 }}>
{marker_package}
{marker_destination}
</GoogleMap>
);
}
}
}
export default withScriptjs(withGoogleMap(Map));
I am using example in react-google-maps library.
const { compose, withProps, lifecycle } = require("recompose");
const {
withScriptjs,
withGoogleMap,
GoogleMap,
DirectionsRenderer,
} = require("react-google-maps");
const MapWithADirectionsRenderer = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route({
origin: new google.maps.LatLng(41.8507300, -87.6512600),
destination: new google.maps.LatLng(41.8525800, -87.6514100),
travelMode: google.maps.TravelMode.DRIVING,
}, (result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result,
});
} else {
console.error(`error fetching directions ${result}`);
}
});
}
})
)(props =>
<GoogleMap
defaultZoom={7}
defaultCenter={new google.maps.LatLng(41.8507300, -87.6512600)}
>
{props.directions && <DirectionsRenderer directions={props.directions} />}
</GoogleMap>
);
<MapWithADirectionsRenderer />
I want to enable alternative routes in my map. So I used
provideRouteAlternatives: true
so inside callback function
(result, status) => { }
the result have a property routes which is an array of alternative routes.
How can I render those routes into map ? .. I also want to click on routes and they will change the color from active to inactive. When user select the route I need to send on the server property called
overview_polyline
which is inside of routes array, where each route inside the array has this property.
Thank you very much.
If you only want to render those routes on the map, you could use DirectionsRenderer from that library.
https://tomchentw.github.io/react-google-maps/#directionsrenderer
However, this DirectionsRenderer component can not be fully customized like defining the colors or onClick functions. What you could do is creating a customized Directions component using Marker and Polygon which also come from this library. Below is how I made it:
import React, { Component } from 'react';
import { Polyline, Marker } from 'react-google-maps';
import { pinkA200, blue500 } from 'material-ui/styles/colors';
import ntol from 'number-to-letter';
import _ from 'lodash';
const DirectionMarker = ({ data, isEnd, i, onClick }) => {
const { start_location, end_location } = data;
if (isEnd) {
return [
<Marker onClick={onClick} position={start_location} label={ntol(i)} key="end0" />,
<Marker onClick={onClick} position={end_location} label={ntol(i + 1)} key="end1" />
];
}
return <Marker onClick={onClick} position={start_location} label={ntol(i)} />;
};
const Direction = ({ direction, isEnd, i, onClick, isSelected }) => {
const data = direction.routes[0].legs[0];
const path = data.steps.reduce((sum, current) => _.concat(sum, current.path), []);
return [
<DirectionMarker data={data} onClick={onClick} isEnd={isEnd} i={i} key="marker" />,
<Polyline
onClick={onClick}
path={path}
options={{
strokeColor: isSelected ? pinkA200 : blue500,
strokeOpacity: 0.6,
strokeWeight: 6
}}
key="line"
/>
];
};
class Directions extends Component {
constructor(props) {
super(props);
this.state = { selectedSegment: 0 };
}
render() {
const { directions } = this.props;
if (_.isEmpty(directions)) {
return false;
}
return directions.map((d, i) => {
const directionProps = {
direction: d,
i,
key: i,
onClick: () => this.setState({ selectedSegment: i }),
isEnd: i === directions.length - 1,
isSelected: i === this.state.selectedSegment
};
return <Direction {...directionProps} />;
});
}
}
export default Directions;
when I trying to include geoocoder api code it shows an error of "TypeError: Cannot read property 'maps' of undefined".
this is my code
import React from 'react';
import { compose, withProps,withHandlers } from "recompose"
import { withScriptjs, withGoogleMap, GoogleMap, Marker,InfoWindow,GeoCoder } from "react-google-maps"
import { MarkerClusterer } from 'react-google-maps/lib/components/addons/MarkerClusterer';
import { connect } from 'react-redux';
let geocoder = new google.maps.Geocoder();
const MyMapComponent = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?AIzaSyAoaDS6fIKlYvEHeTaakCxXqp-UwnggoEgv=3&sensor=true&callback=init",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withHandlers({
onMarkerClustererClick: () => (markerClusterer) => {
const clickedMarkers = markerClusterer.getMarkers()
console.log(`Current clicked markers length: ${clickedMarkers.length}`)
console.log(clickedMarkers)
},
}),
withScriptjs,
withGoogleMap
)((props) =>{
return ( <GoogleMap
zoom={props.maps.zoom}
center={{ lat:props.maps.lat, lng:props.maps.lng }}
heading={5}
>
<MarkerClusterer
onClick={props.onMarkerClustererClick}
averageCenter
enableRetinaIcons
gridSize={60}
>
{props.markers.map(marker => (
<Marker
key={marker.photo_id}
position={{ lat: marker.latitude, lng: marker.longitude }}
/>
))}
</MarkerClusterer>
<Marker
position={{lat:props.maps.lat, lng:props.maps.lng }}
>
<InfoWindow>
<p>gdgd</p>
</InfoWindow>
</Marker>
</GoogleMap>
)});
export default class DemoApp extends React.Component {
componentWillMount() {
this.setState({ markers: [] })
}
componentDidMount() {
console.log("+mymap++++++++");
console.log(this.props.myMap);
this.setState({markers:[{photo_id:1,longitude:76.911270,latitude:11.032595},
{photo_id:2,longitude:75.806682,latitude:11.259169},
{photo_id:3,longitude:77.213780,latitude:28.617671},
{photo_id:4,longitude:78.138991,latitude:9.903245}]})
}
render() {
return (
<MyMapComponent markers={this.state.markers} maps={this.props.myMap} />
)
}
}
In the above code when I trying to create a geocoder variable it shows the error.Here I am trying to get the location name from the latitude and longitude positions of the map using geocoder api.
You might have included google in your index.html, so the google variable is not accessible in your component as it is a window variable.
Try using window.google.maps.xxx, then it must resolve your TypeError
For your particular case
import React from 'react';
...
let geocoder = new window.google.maps.Geocoder(); // edited
const MyMapComponent = compose(
...
The main issue with your code was this fragment:
<MyMapComponent markers={this.state.markers} maps={this.props.myMap} />
I assume that DemoApp was the main application component that you were mounting in the index.js file. You haven't passed any props to the DemoApp component therefore in MyMapComponent a maps prop was undefined. Using props.maps.lat inside of that component caused the TypeError: Cannot read property 'maps' of undefined error to appear.
I noticed some other minor issues:
this code is unnecessary:
import { ..., GeoCoder } from 'react-google-maps';
react-google-maps doesn't even export the component with that name. Maybe it used to, but it no longer does. You can check that in the documentation
this also isn't required:
let geocoder = new google.maps.Geocoder();
and I changed the googleMapURL to the one from the docs:
googleMapURL: 'https://maps.googleapis.com/maps/api/js?key=your_key_goes_here&v=3.exp&libraries=geometry,drawing,places'
You can skip generating the key for now, but there will be a warning in the console.
Working code below:
import React, { Component } from 'react';
import { compose, withProps, withHandlers } from 'recompose';
import { withScriptjs, withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps';
import { MarkerClusterer } from 'react-google-maps/lib/components/addons/MarkerClusterer';
const MyMapComponent = 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%' }} />
}),
withHandlers({
onMarkerClustererClick: () => (markerClusterer) => {
const clickedMarkers = markerClusterer.getMarkers();
console.log(`Current clicked markers length: ${clickedMarkers.length}`);
console.log(clickedMarkers);
}
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap
zoom={props.maps.zoom}
center={{ lat: props.maps.lat, lng: props.maps.lng }}
heading={5}
>
<MarkerClusterer
onClick={props.onMarkerClustererClick}
averageCenter
enableRetinaIcons
gridSize={60}
>
{props.markers.map(marker => (
<Marker key={marker.photo_id} position={{ lat: marker.latitude, lng: marker.longitude }} />
))}
</MarkerClusterer>
<Marker position={{ lat: props.maps.lat, lng: props.maps.lng }}>
<InfoWindow>
<p>test text</p>
</InfoWindow>
</Marker>
</GoogleMap>
));
export default class DemoApp extends Component {
render() {
const markers = [
{ photo_id: 1, longitude: 76.91127, latitude: 11.032595 },
{ photo_id: 2, longitude: 75.806682, latitude: 11.259169 },
{ photo_id: 3, longitude: 77.21378, latitude: 28.617671 }
];
const myMap = {
lng: 78.138991,
lat: 9.903245,
zoom: 6
};
return <MyMapComponent markers={markers} maps={myMap} />;
}
}
I had similar error when were referencing to window.google.maps.Size and this line fixed it.
import { OverlayView } from '#react-google-maps/api'
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.