Skip to content

Commit

Permalink
feat(withGoogleMap): provide HOC for initialize google.maps.Map ins…
Browse files Browse the repository at this point in the history
…tance

* `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')
);
```
  • Loading branch information
tomchentw committed Oct 4, 2016
1 parent 2df62c7 commit f61724c
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 88 deletions.
73 changes: 0 additions & 73 deletions src/lib/GoogleMapLoader.js

This file was deleted.

1 change: 1 addition & 0 deletions src/lib/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const MAP = `__SECRET_MAP_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`;
18 changes: 3 additions & 15 deletions src/lib/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
export { default as GoogleMapLoader } from "./GoogleMapLoader";

export { default as GoogleMap } from "./GoogleMap";
export { default as Circle } from "./Circle";
export { default as DirectionsRenderer } from "./DirectionsRenderer";
export { default as DrawingManager } from "./DrawingManager";
export { default as InfoWindow } from "./InfoWindow";
export { default as KmlLayer } from "./KmlLayer";
export { default as Marker } from "./Marker";
export { default as OverlayView } from "./OverlayView";
export { default as Polygon } from "./Polygon";
export { default as Polyline } from "./Polyline";
export { default as Rectangle } from "./Rectangle";
export { default as SearchBox } from "./SearchBox";
export { default as HeatmapLayer } from "./HeatmapLayer";
export {
default as withGoogleMap,
} from "./withGoogleMap";
106 changes: 106 additions & 0 deletions src/lib/withGoogleMap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/* global google */
import warning from "warning";

import invariant from "invariant";

import getDisplayName from "react-display-name";

import {
default as React,
PropTypes,
Component,
} from "react";

import {
MAP,
} from "./constants";

export default function withGoogleMap(WrappedComponent) {
return class Container extends Component {
static displayName = `withGoogleMap(${getDisplayName(WrappedComponent)})`;

static propTypes = {
containerElement: PropTypes.node.isRequired,
mapElement: PropTypes.node.isRequired,
};

static childContextTypes = {
[MAP]: PropTypes.object,
};

state = {
map: null,
};

handleComponentMount = this.handleComponentMount.bind(this);

getChildContext() {
return {
[MAP]: this.state.map,
};
}

componentWillMount() {
const {
containerElement,
mapElement,
} = this.props;
invariant(!!containerElement && !!mapElement,
`Required props containerElement or mapElement is missing. You need to provide both of them.
The \`google.maps.Map\` instance will be initialized on mapElement and it's wrapped by\
containerElement.\nYou need to provide both of them since Google Map requires the DOM to\
have height when initialized.`
);
}

handleComponentMount(node) {
if (this.state.map || node === null) {
return;
}
warning(`undefined` !== typeof google,
`Make sure you've put a <script> tag in your <head> element to load Google Maps JavaScript API v3.
If you're looking for built-in support to load it for you, use the "async/ScriptjsLoader" instead.
See https://github.com/tomchentw/react-google-maps/pull/168`
);
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#Map
const map = new google.maps.Map(node);
this.setState({ map });
}

render() {
const {
containerElement,
mapElement,
...restProps,
} = this.props;

const {
map,
} = this.state;

if (map) {
return (
React.cloneElement(containerElement, {
},
React.cloneElement(mapElement, {
ref: this.handleComponentMount,
}),
(<div>
<WrappedComponent {...restProps} />
</div>)
)
);
} else {
return (
React.cloneElement(containerElement, {
},
React.cloneElement(mapElement, {
ref: this.handleComponentMount,
}),
(<div />)
)
);
}
}
};
}

0 comments on commit f61724c

Please sign in to comment.