Skip to content

Commit f61724c

Browse files
committed
feat(withGoogleMap): provide HOC for initialize google.maps.Map instance
* `withGoogleMap` requires a prop: `containerElement` * `withGoogleMap` requires a prop: `mapElement` BREAKING CHANGE: Wrap all `react-google-maps` components inside `withGoogleMap` HOC. Before: ```js // v5.0.0 <GoogleMapLoader containerElement={ <div {...this.props} style={{ height: "100%", }} /> } googleMapElement={ <GoogleMap ref={(map) => console.log(map)} defaultZoom={3} defaultCenter={{lat: -25.363882, lng: 131.044922}} onClick={::this.handleMapClick}> {this.state.markers.map((marker, index) => { return ( <Marker {...marker} onRightclick={this.handleMarkerRightclick.bind(this, index)} /> ); })} </GoogleMap> } /> // or v4.0.0 <GoogleMap containerProps={{ ...this.props, style: { height: "100%", }, }} ref="map" defaultZoom={3} defaultCenter={{lat: -25.363882, lng: 131.044922}} onClick={::this.handleMapClick}> {this.state.markers.map((marker, index) => { return ( <Marker {...marker} onRightclick={this.handleMarkerRightclick.bind(this, index)} /> ); })} </GoogleMap> ``` After: ```js // Wrap all `react-google-maps` components with `withGoogleMap` HOC // and name it GettingStartedGoogleMap const GettingStartedGoogleMap = withGoogleMap(props => ( <GoogleMap ref={props.onMapLoad} defaultZoom={3} defaultCenter={{ lat: -25.363882, lng: 131.044922 }} onClick={props.onMapClick} > {props.markers.map((marker, index) => ( <Marker {...marker} onRightClick={() => props.onMarkerRightClick(index)} /> ))} </GoogleMap> )); // Then, render it: render( <GettingStartedGoogleMap containerElement={ <div style={{ height: `100%` }} /> } mapElement={ <div style={{ height: `100%` }} /> } onMapLoad={_.noop} onMapClick={_.noop} markers={markers} onMarkerRightClick={_.noop} />, document.getElementById('root') ); ```
1 parent 2df62c7 commit f61724c

File tree

4 files changed

+110
-88
lines changed

4 files changed

+110
-88
lines changed

src/lib/GoogleMapLoader.js

Lines changed: 0 additions & 73 deletions
This file was deleted.

src/lib/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const MAP = `__SECRET_MAP_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;

src/lib/index.js

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
export { default as GoogleMapLoader } from "./GoogleMapLoader";
2-
3-
export { default as GoogleMap } from "./GoogleMap";
4-
export { default as Circle } from "./Circle";
5-
export { default as DirectionsRenderer } from "./DirectionsRenderer";
6-
export { default as DrawingManager } from "./DrawingManager";
7-
export { default as InfoWindow } from "./InfoWindow";
8-
export { default as KmlLayer } from "./KmlLayer";
9-
export { default as Marker } from "./Marker";
10-
export { default as OverlayView } from "./OverlayView";
11-
export { default as Polygon } from "./Polygon";
12-
export { default as Polyline } from "./Polyline";
13-
export { default as Rectangle } from "./Rectangle";
14-
export { default as SearchBox } from "./SearchBox";
15-
export { default as HeatmapLayer } from "./HeatmapLayer";
1+
export {
2+
default as withGoogleMap,
3+
} from "./withGoogleMap";

src/lib/withGoogleMap.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/* global google */
2+
import warning from "warning";
3+
4+
import invariant from "invariant";
5+
6+
import getDisplayName from "react-display-name";
7+
8+
import {
9+
default as React,
10+
PropTypes,
11+
Component,
12+
} from "react";
13+
14+
import {
15+
MAP,
16+
} from "./constants";
17+
18+
export default function withGoogleMap(WrappedComponent) {
19+
return class Container extends Component {
20+
static displayName = `withGoogleMap(${getDisplayName(WrappedComponent)})`;
21+
22+
static propTypes = {
23+
containerElement: PropTypes.node.isRequired,
24+
mapElement: PropTypes.node.isRequired,
25+
};
26+
27+
static childContextTypes = {
28+
[MAP]: PropTypes.object,
29+
};
30+
31+
state = {
32+
map: null,
33+
};
34+
35+
handleComponentMount = this.handleComponentMount.bind(this);
36+
37+
getChildContext() {
38+
return {
39+
[MAP]: this.state.map,
40+
};
41+
}
42+
43+
componentWillMount() {
44+
const {
45+
containerElement,
46+
mapElement,
47+
} = this.props;
48+
invariant(!!containerElement && !!mapElement,
49+
`Required props containerElement or mapElement is missing. You need to provide both of them.
50+
The \`google.maps.Map\` instance will be initialized on mapElement and it's wrapped by\
51+
containerElement.\nYou need to provide both of them since Google Map requires the DOM to\
52+
have height when initialized.`
53+
);
54+
}
55+
56+
handleComponentMount(node) {
57+
if (this.state.map || node === null) {
58+
return;
59+
}
60+
warning(`undefined` !== typeof google,
61+
`Make sure you've put a <script> tag in your <head> element to load Google Maps JavaScript API v3.
62+
If you're looking for built-in support to load it for you, use the "async/ScriptjsLoader" instead.
63+
See https://github.com/tomchentw/react-google-maps/pull/168`
64+
);
65+
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Map
66+
const map = new google.maps.Map(node);
67+
this.setState({ map });
68+
}
69+
70+
render() {
71+
const {
72+
containerElement,
73+
mapElement,
74+
...restProps,
75+
} = this.props;
76+
77+
const {
78+
map,
79+
} = this.state;
80+
81+
if (map) {
82+
return (
83+
React.cloneElement(containerElement, {
84+
},
85+
React.cloneElement(mapElement, {
86+
ref: this.handleComponentMount,
87+
}),
88+
(<div>
89+
<WrappedComponent {...restProps} />
90+
</div>)
91+
)
92+
);
93+
} else {
94+
return (
95+
React.cloneElement(containerElement, {
96+
},
97+
React.cloneElement(mapElement, {
98+
ref: this.handleComponentMount,
99+
}),
100+
(<div />)
101+
)
102+
);
103+
}
104+
}
105+
};
106+
}

0 commit comments

Comments
 (0)