-
Notifications
You must be signed in to change notification settings - Fork 935
Commit
* Closes #141 * Closes #133 BREAKING CHANGE: GoogleMap with props.containerProps is now deprecated. Use GoogleMapLoader with props.googleMapElement instead We also suggest switching to callback based ref so that you'll get the component instance when it is mounted. Before: ```js <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 <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> } /> ```
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,8 @@ import { | |
} from "react"; | ||
|
||
import { | ||
findDOMNode, | ||
} from "react-dom"; | ||
default as warning, | ||
} from "warning"; | ||
|
||
import { | ||
default as GoogleMapHolder, | ||
|
@@ -15,40 +15,47 @@ import { | |
mapEventPropTypes, | ||
} from "./creators/GoogleMapHolder"; | ||
|
||
import { | ||
default as GoogleMapLoader, | ||
} from "./GoogleMapLoader"; | ||
|
||
const USE_NEW_BEHAVIOR_TAG_NAME = `__new_behavior__`; | ||
|
||
export default class GoogleMap extends Component { | ||
static propTypes = { | ||
containerTagName: PropTypes.string.isRequired, | ||
containerProps: PropTypes.object.isRequired, | ||
containerTagName: PropTypes.string, | ||
containerProps: PropTypes.object, | ||
map: PropTypes.object, | ||
// Uncontrolled default[props] - used only in componentDidMount | ||
...mapDefaultPropTypes, | ||
// Controlled [props] - used in componentDidMount/componentDidUpdate | ||
...mapControlledPropTypes, | ||
// Event [onEventName] | ||
...mapEventPropTypes, | ||
} | ||
}; | ||
|
||
// Public APIs | ||
// | ||
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Map | ||
// | ||
// [].map.call($0.querySelectorAll("tr>td>code"), function(it){ return it.textContent; }).filter(function(it){ return it.match(/^get/) && !it.match(/Map$/); }) | ||
getBounds () { return this.state.map.getBounds(); } | ||
getBounds () { return (this.props.map || this.refs.delegate).getBounds(); } | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
tomchentw
Author
Owner
|
||
|
||
getCenter () { return this.state.map.getCenter(); } | ||
getCenter () { return (this.props.map || this.refs.delegate).getCenter(); } | ||
|
||
getDiv () { return this.state.map.getDiv(); } | ||
getDiv () { return (this.props.map || this.refs.delegate).getDiv(); } | ||
|
||
getHeading () { return this.state.map.getHeading(); } | ||
getHeading () { return (this.props.map || this.refs.delegate).getHeading(); } | ||
|
||
getMapTypeId () { return this.state.map.getMapTypeId(); } | ||
getMapTypeId () { return (this.props.map || this.refs.delegate).getMapTypeId(); } | ||
|
||
getProjection () { return this.state.map.getProjection(); } | ||
getProjection () { return (this.props.map || this.refs.delegate).getProjection(); } | ||
|
||
getStreetView () { return this.state.map.getStreetView(); } | ||
getStreetView () { return (this.props.map || this.refs.delegate).getStreetView(); } | ||
|
||
getTilt () { return this.state.map.getTilt(); } | ||
getTilt () { return (this.props.map || this.refs.delegate).getTilt(); } | ||
|
||
getZoom () { return this.state.map.getZoom(); } | ||
getZoom () { return (this.props.map || this.refs.delegate).getZoom(); } | ||
// END - Public APIs | ||
// | ||
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Map | ||
|
@@ -59,55 +66,50 @@ export default class GoogleMap extends Component { | |
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Map | ||
// | ||
// [].map.call($0.querySelectorAll("tr>td>code"), function(it){ return it.textContent; }).filter(function(it){ return !it.match(/^get/) && !it.match(/^set/) && !it.match(/Map$/); }) | ||
fitBounds (bounds) { return this.state.map.fitBounds(bounds); } | ||
fitBounds (bounds) { return (this.props.map || this.refs.delegate).fitBounds(bounds); } | ||
|
||
panBy (x, y) { return this.state.map.panBy(x, y); } | ||
panBy (x, y) { return (this.props.map || this.refs.delegate).panBy(x, y); } | ||
|
||
panTo (latLng) { return this.state.map.panTo(latLng); } | ||
panTo (latLng) { return (this.props.map || this.refs.delegate).panTo(latLng); } | ||
|
||
panToBounds (latLngBounds) { return this.state.map.panToBounds(latLngBounds); } | ||
panToBounds (latLngBounds) { return (this.props.map || this.refs.delegate).panToBounds(latLngBounds); } | ||
// END - Public APIs - Use this carefully | ||
// | ||
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Map | ||
|
||
static defaultProps = { | ||
containerTagName: "div", | ||
containerProps: {}, | ||
} | ||
|
||
state = { | ||
} | ||
componentWillMount () { | ||
const {containerTagName} = this.props; | ||
const isUsingNewBehavior = USE_NEW_BEHAVIOR_TAG_NAME === containerTagName; | ||
|
||
componentDidMount () { | ||
const domEl = findDOMNode(this); | ||
const {containerTagName, containerProps, children, ...mapProps} = this.props; | ||
// TODO: support asynchronous load of google.maps API at this level. | ||
// | ||
// Create google.maps.Map instance so that dom is initialized before | ||
// React's children creators. | ||
// | ||
const map = GoogleMapHolder._createMap(domEl, mapProps); | ||
this.setState({ map }); | ||
warning(isUsingNewBehavior, | ||
`"GoogleMap" with containerTagName is deprecated now and will be removed in next major release (5.0.0). | ||
Use "GoogleMapLoader" instead. See https://github.com/tomchentw/react-google-maps/pull/157 for more details.` | ||
); | ||
} | ||
|
||
render () { | ||
const {containerTagName, containerProps, children, ...mapProps} = this.props; | ||
const child = this.state.map ? ( | ||
// Notice: implementation details | ||
// | ||
// In this state, the DOM of google.maps.Map is already initialized in | ||
// my innerHTML. Adding extra React components will not clean it | ||
// in current version*. It will use prepend to add DOM of | ||
// GoogleMapHolder and become a sibling of the DOM of google.maps.Map | ||
// Not sure this is subject to change | ||
// | ||
// *current version: 0.13.3, 0.14.2 | ||
// | ||
<GoogleMapHolder map={this.state.map} {...mapProps}> | ||
{children} | ||
</GoogleMapHolder> | ||
) : undefined; | ||
|
||
return React.createElement(containerTagName, containerProps, child); | ||
const {containerTagName, containerProps = {}, children, ...mapProps} = this.props; | ||
const isUsingNewBehavior = USE_NEW_BEHAVIOR_TAG_NAME === containerTagName; | ||
|
||
if (isUsingNewBehavior) { | ||
return ( | ||
<GoogleMapHolder {...mapProps}> | ||
{children} | ||
</GoogleMapHolder> | ||
); | ||
} else {//------------ Deprecated ------------ | ||
const realContainerTagName = null == containerTagName ? `div` : containerTagName; | ||
|
||
return ( | ||
<GoogleMapLoader | ||
containerElement={React.createElement(realContainerTagName, containerProps)} | ||
googleMapElement={ | ||
<GoogleMap ref="delegate" containerTagName={USE_NEW_BEHAVIOR_TAG_NAME} {...mapProps}> | ||
{children} | ||
</GoogleMap> | ||
} | ||
/> | ||
); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { | ||
default as React, | ||
PropTypes, | ||
Component, | ||
} from "react"; | ||
|
||
import { | ||
default as propTypesElementOfType, | ||
} from "react-prop-types-element-of-type"; | ||
|
||
import { | ||
default as GoogleMapHolder, | ||
} from "./creators/GoogleMapHolder"; | ||
|
||
const USE_NEW_BEHAVIOR_TAG_NAME = `__new_behavior__`;/* CIRCULAR_DEPENDENCY */ | ||
|
||
export default class GoogleMapLoader extends Component { | ||
static propTypes = { | ||
containerElement: PropTypes.node.isRequired, | ||
This comment has been minimized.
Sorry, something went wrong.
maciejmyslinski
|
||
googleMapElement: PropTypes.element.isRequired,/* CIRCULAR_DEPENDENCY. Uncomment when 5.0.0 comes: propTypesElementOfType(GoogleMap).isRequired, */ | ||
}; | ||
|
||
static defaultProps = { | ||
containerElement: (<div />), | ||
}; | ||
|
||
state = { | ||
map: null, | ||
}; | ||
|
||
mountGoogleMap (domEl) { | ||
if (this.state.map) { | ||
return; | ||
} | ||
const {children, ...mapProps} = this.props.googleMapElement.props; | ||
// | ||
// Create google.maps.Map instance so that dom is initialized before | ||
// React's children creators. | ||
// | ||
const map = GoogleMapHolder._createMap(domEl, mapProps); | ||
this.setState({ map }); | ||
} | ||
|
||
renderChild () { | ||
if (this.state.map) { | ||
// Notice: implementation details | ||
// | ||
// In this state, the DOM of google.maps.Map is already initialized in | ||
// my innerHTML. Adding extra React components will not clean it | ||
// in current version*. It will use prepend to add DOM of | ||
// GoogleMapHolder and become a sibling of the DOM of google.maps.Map | ||
// Not sure this is subject to change | ||
// | ||
// *current version: 0.13.3, 0.14.2 | ||
// | ||
return React.cloneElement(this.props.googleMapElement, { | ||
map: this.state.map, | ||
//------------ Deprecated ------------ | ||
containerTagName: USE_NEW_BEHAVIOR_TAG_NAME, | ||
}); | ||
} | ||
} | ||
|
||
render () { | ||
return React.cloneElement(this.props.containerElement, { | ||
ref: ::this.mountGoogleMap, | ||
}, this.renderChild()); | ||
} | ||
} |
3 comments
on commit 532816a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why this change?
I'm learning a lot, from your code, you are good programmer.
BIG thank you for that library! 👍
Edit
I've seen issues that this commit closes, thank You one more time! 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why for this change?
This PR decouples the creation logic of google.maps.Map
instance and the mounting logic for <GoogleMap>
component. As a result, we'll have:
The correct lifecycle for <GoogleMap>
component
Because when it's mounted (ref
callback invoked with the component instance), it's ready for use: invoking public APIs, reading values with getters ...etc
Consistent API with ScriptjsLoader
See more in the example below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maciejmyslinski thanks for the feedback!
Just curious how You achived autobinding here... Could you explain, please?