Updating child prop breaks connection with parent state - reactjs

I'm using react-google-maps to display markers on a map. When I use the SearchBox function in the child component to set props.locations my map Markers appear. However, when I use a button in the parent component to update the state of the locations within MapWithASearchBox the child prop doesn't recognise the change. If I reload the page and just use the button on the parent Component the markers appear correct. I assume I'm breaking the connection between child and parent when I set the prop within the child component.
Path: Parent Component MapPage.jsx
export default class MapPage extends Component {
constructor(props) {
super(props);
this.state = {
companies: []
}
this.handleButtonASXMarketCap = this.handleButtonASXMarketCap.bind(this);
}
handleButtonASXMarketCap(limit, e) {
e.preventDefault();
Meteor.call('getASXTop100', limit, (error, result) => {
if (result) {
this.setStateOfCompanies(result);
}
});
}
setStateOfCompanies(data) {
this.setState({
companies: data
})
}
render() {
return (
<div>
<Button color="primary ml-2" onClick={(e) => this.handleButtonASXMarketCap(100, e)}>Top 100 ASX</Button>
<MapWithASearchBox
locations={this.state.companies}
/>
</div>
);
}
}
Path: Child Component MapWithASearchBox.jsx
const MapWithASearchBox = compose(
withProps({
googleMapURL: "myUrl",
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: -23.696248, lng: 133.880731
},
zoom: 4,
markers: [],
onPlacesChanged: () => {
const places = refs.searchBox.getPlaces();
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,
zoom: 15
});
const lat = nextMarkers.map(x => x.position.lat());
const lng = nextMarkers.map(x => x.position.lng());
Meteor.call('runMethodTest', lat[0], lng[0], (error, result) => {
if (result) {
this.setState({
locations: result
});
}
});
},
})
},
}),
withScriptjs,
withGoogleMap
)(props =>
<GoogleMap>
<SearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
controlPosition={google.maps.ControlPosition.TOP_LEFT}
onPlacesChanged={props.onPlacesChanged}
>
<input
type="text"
placeholder="Customized your placeholder"
/>
</SearchBox>
{props.locations.map((location, index) => (
<span key={location._id.location.lng}>
<Marker
position={location._id.location}
onClick={() => props.onToggleOpen(index)}
/>
</span>
))}
</GoogleMap>
);

Related

Get details of all the Markers covered under a drawn polygon or any shape using react-google-maps

I am using react-google-maps. I have created map and displayed 3 types of markers in it.
I can now draw polygons on map but not able to get the details of markers covered under the drawn shape.
Any help would be appreciated.
This is my code of map jsx.
I am creating 3 different markers with different icons to identify on map.
When i draw a shape on map, i want to get the details of every sort of marker which comes under the drawn shape.
import React from "react";
import DrawingManager from "react-google-maps/lib/components/drawing/DrawingManager";
import {GoogleMap, InfoWindow, Marker, withGoogleMap, withScriptjs} from "react-google-maps";
import UploadApis from "../../service/rest/upload";
import "./index.scss";
let selectedShape;
function clearSelection() {
if (selectedShape) {
if (selectedShape.type !== 'marker') {
selectedShape.setEditable(false);
}
selectedShape = null;
}
}
function setSelection(shape) {
if (shape.type !== 'marker') {
clearSelection();
shape.setEditable(true);
}
selectedShape = shape;
}
class Map extends React.Component {
constructor(props) {
super(props);
this.shapes = [];
this.state = {
fiberData: [],
subscriberData: [],
sitesData: [],
fetchData: false,
selected: null
};
this.handleOverlayComplete = this.handleOverlayComplete.bind(this);
this.data();
}
handleOverlayComplete(e) {
console.log("overlay",e);
const newShape = e.overlay;
newShape.type = e.type;
if (e.type !== window.google.maps.drawing.OverlayType.MARKER) {
window.google.maps.event.addListener(newShape, 'click', function (e) {
if (e.vertex !== undefined) {
if (newShape.type === window.google.maps.drawing.OverlayType.POLYGON) {
let path = newShape.getPaths().getAt(e.path);
path.removeAt(e.vertex);
if (path.length < 3) {
newShape.setMap(null);
}
}
if (newShape.type === window.google.maps.drawing.OverlayType.POLYLINE) {
let path = newShape.getPath();
path.removeAt(e.vertex);
if (path.length < 2) {
newShape.setMap(null);
}
}
}
setSelection(newShape);
});
setSelection(newShape);
} else {
window.google.maps.event.addListener(newShape, 'click', function (e) {
setSelection(newShape);
});
setSelection(newShape);
}
this.shapes.push(newShape);
}
data = async () => {
let fiberData = await UploadApis.getMetaDataById("fiber",this.props.projectId);
let sitesData = await UploadApis.getMetaDataById("sites",this.props.projectId);
let subscriberData = await UploadApis.getMetaDataById("subscriber",this.props.projectId);
this.setState({fiberData: fiberData, sitesData: sitesData, subscriberData: subscriberData, fetchData: true})
};
deleteSelectedShape = () => {
if (selectedShape) {
selectedShape.setMap(null);
}
};
setSelected(selected) {
this.setState({selected: selected})
}
render() {
return (
<div>
<button className="btn-container" onClick={this.deleteSelectedShape}>Delete Shape
</button>
{this.state.fetchData ?
<div>
<GoogleMap
defaultZoom={6}
defaultCenter={{lat: 22.5106879, lng: 79.9189213}}
>
<DrawingManager
defaultDrawingMode={null}
defaultOptions={{
drawingControl: true,
drawingControlOptions: {
position: window.google.maps.ControlPosition.TOP_CENTER,
drawingModes: [ 'circle', 'polygon', 'polyline', 'rectangle']
},
polygonOptions: {editable: true},
circleOptions: {editable: true},
rectangleOptions: {editable: true},
markerOptions: {editable: true},
polylineOptions: {editable: true}
}}
onOverlayComplete={this.handleOverlayComplete}
/>
{this.state.fiberData.map((fiber) => (
<Marker
key={fiber.id}
position={{lat: fiber.latitude, lng: fiber.longitude}}
onClick={() => {
this.setSelected(fiber);
}}
icon={{
url: "../assets/svg/fiber.png",
scaledSize: new window.google.maps.Size(25, 25)
}}
/>
))}
{this.state.sitesData.map((site) => (
<Marker
key={site.id}
position={{lat: site.latitude, lng: site.longitude}}
onClick={() => {
this.setSelected(site);
}}
icon={{
url: "../assets/svg/tower.png",
scaledSize: new window.google.maps.Size(25, 25)
}}
/>
))}
{this.state.subscriberData.map((subscriber) => (
<Marker
key={subscriber.id}
position={{lat: subscriber.latitude, lng: subscriber.longitude}}
onClick={() => {
this.setSelected(subscriber);
}}
icon={{
url: "../assets/svg/subscriber.svg",
scaledSize: new window.google.maps.Size(25, 25)
}}
/>
))}
{this.state.selected && (
<InfoWindow
position={{lat: this.state.selected.latitude, lng: this.state.selected.longitude}}
onCloseClick={() => {
this.setSelected(null);
}}>
<div>
<h4>{this.state.selected.name}</h4>
<p>{this.state.selected.description}</p>
</div>
</InfoWindow>
)}
</GoogleMap>
</div> : null
}
</div>
);
}
}
export default withScriptjs(withGoogleMap(Map));
This is the first file which i have called. Index file which renders the map is -
import React from "react";
import Map from './map.jsx';
export default class MapContainer extends React.Component {
_projectId="";
constructor(props) {
super(props);
console.log(props);
if(props.location.state.project){
this._projectId = props.location.state.project.id;
}
}
render() {
return (
<div>
<Map
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=API_KEY`}
loadingElement={<div style={{height: `100%`}}/>}
containerElement={<div style={{height: `480px`}}/>}
mapElement={<div style={{height: `100%`}}/>}
projectId ={this._projectId}
/>
</div>
);
}
}```

React testing with Jest and Enzyme #react-google-maps/api returns TypeError: Cannot read property 'maps' of undefined

I am trying to test a component with #react-google-maps/api package. I am getting error: TypeError: Cannot read property 'maps' of undefined.
My component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPersonId, updatePersonSettings, pushPersonMessage } from '../../actions/person';
import TemplatePage from '../templates/TemplatePage';
import Card from '../partials/Card';
import Msg from '../partials/Msg';
import { GoogleMap, Marker } from '#react-google-maps/api';
import markerPosition from'../../img/marker-position.png';
import PlacesAutocomplete, { geocodeByAddress, getLatLng } from 'react-places-autocomplete';
import PropTypes from 'prop-types';
import { DEFAULT_LAT, DEFAULT_LNG, DEFAULT_ZOOM } from '../../config/';
export class Settings extends Component {
state = {
lat: DEFAULT_LAT,
lng: DEFAULT_LNG,
zoom: DEFAULT_ZOOM,
address: '',
formSubmitted: false
}
componentDidMount () {
const { lat, lng, zoom } = this.props.auth;
this.setState({
lat: lat !== undefined && lat !== null ? lat : DEFAULT_LAT,
lng: lng !== undefined && lng !== null ? lng : DEFAULT_LNG,
zoom: zoom !== undefined && zoom !== null ? zoom : DEFAULT_ZOOM
});
this.drawMarker();
}
handleOnSubmit = e => {
e.preventDefault();
const settings = {
zoom: this.state.zoom,
lat: this.state.lat,
lng: this.state.lng
}
this.props.updatePersonSettings({ id: this.props.auth.person_id, settings })
}
handleChangeZoom = event => {
this.setState({ zoom: parseInt(event.target.value )});
}
handleChangeAddress = (address) => {
this.setState({ address });
}
handleSelect = (address) => {
geocodeByAddress(address)
.then(results =>
getLatLng(results[0])
.then(function(result) {
this.setState({
lat: result.lat,
lng: result.lng,
})
this.drawMarker()
}.bind(this))
)
.catch(error => console.error('Error', error));
};
handleMapClick = e => {
this.setState({
lat: e.latLng.lat(),
lng: e.latLng.lng(),
});
this.drawMarker();
}
handleMapZoom = (zoom) => {
console.log(zoom)
}
drawMarker = () => {
return <Marker
position={{
lat: parseFloat(this.state.lat),
lng: parseFloat(this.state.lng)
}}
icon={
new window.google.maps.MarkerImage(
markerPosition,
null, /* size is determined at runtime */
null, /* origin is 0,0 */
null, /* anchor is bottom center of the scaled image */
new window.google.maps.Size(48, 48)
)
}
>
</Marker>
}
get msg() {
if(this.props.person !== '') {
return <Msg msg={this.props.person} />
}
return null;
}
render() {
const { status } = this.props.person;
const { lat, lng, zoom, address, formSubmitted } = this.state;
return (
<TemplatePage>
{ this.msg }
<Card title='Settings' padding='large'>
<form className="form" onSubmit={this.handleOnSubmit}>
<div className="form-group">
<label htmlFor="position">Default map position</label>
<div className="google-map google-map__settings">
<GoogleMap
center={{ lat, lng }}
zoom={ zoom }
onClick={ e => this.handleMapClick(e) }
onZoomChanged={(e) => {
console.log('zoom changed')
}}
>
{this.drawMarker()}
<div className="map-constraints-container" />
</GoogleMap>
</div>
</div>
<div className="form-group">
<div className="map-constraints-slider">
<label htmlFor="range">Default map zoom: {zoom}</label>
<input
type="range"
id="zoom"
value={ zoom }
name="zoom"
min="1"
max="18"
onChange={ this.handleChangeZoom }
/>
</div>
</div>
<div className="form-group">
<PlacesAutocomplete
value={address}
onChange={ this.handleChangeAddress }
onSelect={ this.handleSelect }
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<input
{...getInputProps({
placeholder: 'Search places...',
className: 'location-search-input',
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map(suggestion => {
const className = suggestion.active
? 'suggestion-item--active'
: 'suggestion-item';
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: '#fafafa', cursor: 'pointer' }
: { backgroundColor: '#ffffff', cursor: 'pointer' };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style,
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
</div>
<div className="form-group">
<input
type="submit"
value="Update settings"
className="btn btn--primary card__footer--btn-left"
disabled={ formSubmitted && status === 'fetching' ? 'disabled' : null }
/>
</div>
</form>
</Card>
</TemplatePage>
)
}
}
Settings.defaultProps = {
auth: {},
person: {},
fetchPersonId: () => Promise.resolve(),
updatePersonsettings: () => Promise.resolve(),
pushPersonMessage: () => Promise.resolve()
}
Settings.propTypes = {
auth: PropTypes.object,
person: PropTypes.object,
fetchPersonId: PropTypes.func,
updatePersonsettings: PropTypes.func,
pushPersonMessage: PropTypes.func
};
export default connect(
({ auth, person }) => ({ auth, person }),
{ fetchPersonId, updatePersonSettings, pushPersonMessage }
)(Settings);
My test:
import React from 'react';
import { shallow } from 'enzyme';
import { Settings } from '../../../components/pages/Settings';
test('should render settings page', () => {
const wrapper = shallow(<Settings />);
expect(wrapper).toMatchSnapshot();
});
I read that in order to resolve such issues it is best to mock up the package. In some other component I am using `` package, which I managed to mock like so:
const zxcvbn = require.requireActual('zxcvbn');
export default (password = 'test') => {
return zxcvbn(password);
}
How would I mock up the #react-google-maps/api package and get rid of the error? Is this a good approach (mocking the package)? Or can this be resolved any other way? How would I test if the map or marker rendered at all?
I know this is an old question.
But the way I manage this is by mocking the google object. This is only mocks what I'm calling so you'd need to add any google constants and methods you call.
As for testing if it gets rendered, that's all buried in the google maps javascript api blob. You could add spys to ensure the appropriate functions are called.
I don't think it'd be possible to verify google maps is actually rendering anything though.
import ReactDOM from 'react-dom';
//ReactDOM.createPortal = jest.fn(node => node);
jest.mock('#react-google-maps/api', () => {
const React = require('React');
return {
withGoogleMap: (Component) => Component,
withScriptjs: (Component) => Component,
Polyline: (props) => <div />,
Marker: (props) => <div />,
GoogleMap: (props) => (<div><div className="mock-google-maps" />{props.children}</div>),
};
});
global.google = {
maps: {
LatLngBounds: () => ({
extend: () => { },
}),
MapTypeId: {
ROADMAP: 'rdmap',
SATELLITE: 'stllte'
},
ControlPosition: {
BOTTOM_CENTER: 'BC',
BOTTOM_LEFT: 'BL',
BOTTOM_RIGHT: 'BR',
LEFT_BOTTOM: 'LB',
LEFT_CENTER: 'LC',
LEFT_TOP: 'LT',
RIGHT_BOTTOM: 'RB',
RIGHT_CENTER: 'RC',
RIGHT_TOP: 'RT',
TOP_CENTER: 'TC',
TOP_LEFT: 'TL',
TOP_RIGHT: 'TR',
},
Size: function (w, h) {},
Data: class {
setStyle() {}
addListener() {}
setMap() {}
}
}
};

Dynamic Mapbox layer update using #urbica/react-map-gl

I can’t change the source-layer dynamically in <Layer source-layer={this.state.layer} />. By pressing a button I change state to layer: 'building' or layer: 'road' but changes do not occur inside <Layer source-layer={this.state.layer} />
Code with example: https://codesandbox.io/s/compassionate-brook-l1psj
class App extends React.Component {
state = {
mapStyle: "mapbox://styles/mapbox/light-v9",
viewport: {
latitude: 44.8016,
longitude: -68.7712,
zoom: 15
},
layer: "building"
};
render() {
const { mapStyle } = this.state;
return (
<div className="App">
<div>
<button
onClick={() => {
if (this.state.layer === "road") {
this.setState(state => {
return {
...state,
layer: "building"
};
});
} else {
this.setState(state => {
return {
...state,
layer: "road"
};
});
}
}}
>
Change Style
</button>
</div>
<MapGL
style={{ width: "100%", height: "400px" }}
mapStyle={mapStyle}
accessToken={MAPBOX_ACCESS_TOKEN}
onViewportChange={viewport =>
this.setState(state => {
return {
...state,
viewport
};
})
}
{...this.state.viewport}
>
<Source
id="maine"
type="vector"
url="mapbox://mapbox.mapbox-streets-v8"
/>
{console.log(this.state.layer)} // Here I see that the state is changing
<Layer
id="maine"
type="fill"
source="maine"
source-layer={this.state.layer}
paint={{
"fill-color": "#088",
"fill-opacity": 0.8
}}
/>
</MapGL>
</div>
);
}
}
This issue was resolved. Thanks.
https://github.com/urbica/react-map-gl/issues/273

Make Search Box Functional

I'm doing a map project in React and using the google-maps-react api. I am able to type characters in the search box, but it doesn't filter my list or markers. How can I make that work?
Here's the code for my App.js. I have the updateQuery which should update with whatever is typed in the search box. filterItems is supposed to filter all the locations. addRealMarkers is supposed to replace with the filtered markers:
var foursquare = require("react-foursquare")({
clientID: "BTMAGTC2Y5G1IXAKA4VN4QN55R2DSN1105Y1XGHB0WZ5THHR",
clientSecret: "4HOKQ0ON1V1XEHKSUSEABQMNRFZGCGPIKIUIE5JMUMWVRG5W",
url: "https://api.foursquare.com/v2/venues/search?"
});
var params = {
ll: "31.462170,-97.195732",
query: "Hewitt"
};
class App extends Component {
/* Basic state object that must be kept at the highest "parent" level per
Doug Brown's training video */
constructor(props) {
super(props);
this.state = {
lat: 31.46217,
lon: -97.195732,
zoom: 13,
items: [],
filtered: null,
open: false,
selectedId: null,
activeMarker: null
};
}
realMarkers = [];
componentDidMount() {
foursquare.venues.getVenues(params).then(res => {
this.setState({ items: res.response.venues });
});
fetch("react-foursquare")
.then(response => response.json())
.then(response => {
const items = json.response.items;
this.setState({
items,
filtered: this.filterItems(items, "")
});
})
.catch(error => {
alert("Foursquare data could not be retrieved");
});
}
//Fetches the locations requested for this map.
/*fetchPlaces(mapProps, map) {
const { google } = mapProps;
const service = new google.maps.places.PlacesService(map);
}
//fetch Foursquare API data and use Axios to catch errors, instructed by
Yahya Elharony.
// Source: https://github.com/foursquare/react-foursquare
getPlaces = () => {
const endPoint = "https://api.foursquare.com/v2/venues/explore?";
const params = {
client_id: "BTMAGTC2Y5G1IXAKA4VN4QN55R2DSN1105Y1XGHB0WZ5THHR",
client_secret: "4HOKQ0ON1V1XEHKSUSEABQMNRFZGCGPIKIUIE5JMUMWVRG5W",
near: "Hewitt",
query: "query",
v: 20181117
};
// axios site: https://www.npmjs.com/package/axios
axios
.get(endPoint + new URLSearchParams(params))
.then(response => {
this.setState(
{
venues: response.data.response.groups[0].items
},
this.fetchPlaces()
);
})
.catch(error => {
console.log("ERROR! " + error);
});
};*/
// Creating the replacement markers that goes with the list. Based on my
1:1 training from Doug Brown
addRealMarker = marker => {
let checkList = this.realMarkers.filter(
m => m.marker.id === marker.marker.id
);
if (!checkList.length) this.realMarkers.push(marker);
};
updateQuery = query => {
this.setState({
selectedIndex: null,
filtered: this.filterItems(this.state.items, query)
});
};
filterItems = (items, query) => {
return items.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
);
};
clickListItem = id => {
const marker = this.realMarkers.filter(
marker => marker.marker.id === id
)[0];
this.setState({
selectedId: id,
activeMarker: marker
});
};
/*Google Maps React Component courtesy of
https://www.npmjs.com/package/google-maps-react*/
render() {
const style = {
width: "100%",
height: "100%"
};
return (
<div className="App">
<HewittMap
lat={this.state.lat}
lng={this.state.lng}
zoom={this.state.zoom}
style={style}
items={this.state.items}
addRealMarker={this.addRealMarker}
activeMarker={this.state.activeMarker}
clickListItem={this.clickListItem}
/>
<Sidebar
items={this.state.items}
clickListItem={this.clickListItem}
filterItems={this.updateQuery}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
export default App;
And here's the Sidebar Code. Added another updateQuery function that's supposed to call the props then you'll see some more code in the InputBase component:
class Sidebar extends Component {
state = {
mobileOpen: false,
query: ""
};
handleDrawerOpen = () => {
this.setState({ open: true });
};
handleDrawerClose = () => {
this.setState({ open: false });
};
updateQuery = newQuery => {
// Save the new query string in state and pass the string up the call
tree
this.setState({ query: newQuery });
this.props.filterItems(newQuery);
};
render() {
const { classes, theme } = this.props;
const { open } = this.state;
const items = this.props.items;
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={classNames(classes.appBar, {
[classes.appBarShift]: open
})}
>
<Toolbar disableGutters={!open}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerOpen}
className={classNames(classes.menuButton, open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" color="inherit" noWrap>
City of Hewitt
</Typography>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon places={this.state.places} />
</div>
<InputBase
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
placeholder="Search…"
name="filter"
type="text"
value={this.state.query}
onChange={e => {
this.updateQuery(e.target.value);
}}
/>
</div>
</Toolbar>
</AppBar>
<Drawer
className={classes.drawer}
variant="persistent"
anchor="left"
open={open}
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.drawerHeader}>
<IconButton onClick={this.handleDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</div>
<Divider />
<List>
{this.props.items &&
this.props.items.map((item, index) => {
return (
<ListItem key={item.id}>
<button
key={index}
onClick={e => this.props.clickListItem(item.id)}
>
<ListItemText primary={item.name}> </ListItemText>
</button>
</ListItem>
);
})}
</List>
<Divider />
</Drawer>
<main
className={classNames(classes.content, {
[classes.contentShift]: open
})}
>
<div className={classes.drawerHeader} />
</main>
</div>
);
}
}
Sidebar.propTypes = {
classes: PropTypes.object.isRequired,
// Injected by the documentation to work in an iframe.
// You won't need it on your project.
container: PropTypes.object,
theme: PropTypes.object.isRequired
};
export default withStyles(styles, { withTheme: true })(Sidebar);
You can click in my CodeSandbox to see for yourself.
You are filtering your data and assigning it to filtered but you use items to drive your map, not filtered. It would need more refactoring, but what if you did this?
updateQuery = query => {
this.setState({
selectedIndex: null,
//filtered: this.filterItems(this.state.items, query) // -
items: this.filterItems(this.state.items, query) // +
});
};
You might want an indicator, say isFiltered, that is true when the search bar has a value in it. If true, use the filtered data, else, use the original items

Use react-google-maps methods with recompose

I'm using react google maps for fine tuning the user location after a geo location function, this is part of my Map.js code:
const MyMapComponent = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=AIzaSyAKCqAqiyop85LNl9qUb6OAT1lJupLEnzo&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{height: `100%`}}/>,
containerElement: <div style={{height: `400px`}}/>,
mapElement: <div style={{height: `100%`}}/>,
}),
withState(),
withHandlers(() => {
const refs = {
map: undefined,
}
return {
onMapMounted: () => ref => {
refs.map = ref
},
onBoundsChanged: ({onBoundsChanged}) => () => {
onBoundsChanged(refs.map.getCenter().lat(), refs.map.getCenter().lng())
}
}
}),
withScriptjs,
withGoogleMap
)((props) =>
<GoogleMap
defaultZoom={8}
defaultCenter={{lat: props.lat, lng: props.lng}}
ref={props.onMapMounted}
onBoundsChanged={props.onBoundsChanged}
>
<Marker position={{lat: props.lat, lng: props.lng}}/>
</GoogleMap>
)
I just want to be able to call the panTo() method in other component via ref, internally i have no problem cause i think the ref passes fine in the mapMounted method ( refs.map.getCenter().lat() ), but how to call thee methods externally, my prob is that im using recompose library. thx in advance.
part of the code in Home.js where i use map and have the get position method that i want to trigger panTo():
import MyMapComponent from './Maps';
export default class HomeComponent extends React.Component {
render() {
const {showAlert, address, lat, lng} = this.props;
return (
<MyMapComponent
lat={lat}
lng={lng}
onBoundsChanged={this.props.handleOnBoundsChanged}
/>
<Button onClick={()=>this.props.getPosition()}>Usar mi
ubicación actual</Button>
)
}
}
You can create a prop out of any of the map methods.
...
withHandlers(() => {
let map;
return {
onMapMounted: () => ref => {
map = ref;
},
onBoundsChanged: ({ onBoundsChanged }) => () => {
onBoundsChanged(map.getCenter().lat(), map.getCenter().lng());
}
mapPanTo: () => args => map.panTo(args),
};
})
...
Now props.mapPanTo can be passed around to any other component.

Resources