Reputation: 57421
Using the react-google-maps
example with the GroundOverlay
component as an example (cf. https://github.com/khpeek/beomaps/blob/master/src/App.js), I'd like to make a slider which changes the opacity of the overlay.
For example, currently, if I render this component, I get an overlay with a fixed opacity of 0.5
:
/* global google */
import React from "react"
import { compose } from "recompose"
import {
withScriptjs,
withGoogleMap,
GoogleMap,
GroundOverlay } from "react-google-maps"
export const MapWithGroundOverlay = compose(
withScriptjs,
withGoogleMap
)(props =>
<GoogleMap
defaultZoom={12}
defaultCenter={{lat: 40.740, lng: -74.18}}
>
<GroundOverlay
defaultUrl="https://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg"
defaultBounds={new google.maps.LatLngBounds(
new google.maps.LatLng(40.712216, -74.22655),
new google.maps.LatLng(40.773941, -74.12544)
)}
defaultOpacity={.5}
/>
</GoogleMap>
);
which looks like this:
I'm struggling to see, however, how to change the opacity 'dynamically' with a slider. Ultimately, I believe I would have to call the setOpacity
method on the instance of the google.maps.GroundOverlay
class (cf. https://developers.google.com/maps/documentation/javascript/reference/image-overlay#GroundOverlay). However, if I look at the source code for the <GroundOverlay>
component (cf. https://github.com/tomchentw/react-google-maps/blob/ca5c5c6f5346fb3ee0037893fbca488a408c9938/lib/components/GroundOverlay.js#L59),
var GroundOverlay = (exports.GroundOverlay = (function(_React$PureComponent) {
;(0, _inherits3.default)(GroundOverlay, _React$PureComponent)
/*
* @see https://developers.google.com/maps/documentation/javascript/3.exp/reference#GroundOverlay
*/
function GroundOverlay(props, context) {
;(0, _classCallCheck3.default)(this, GroundOverlay)
var _this = (0, _possibleConstructorReturn3.default)(
this,
(
GroundOverlay.__proto__ || (0, _getPrototypeOf2.default)(GroundOverlay)
).call(this, props, context)
)
;(0, _warning2.default)(
!props.url || !props.bounds,
"\nFor GroundOveray, url and bounds are passed in to constructor and are immutable\n after iinstantiated. This is the behavior of Google Maps JavaScript API v3 (\n See https://developers.google.com/maps/documentation/javascript/reference#GroundOverlay)\n Hence, use the corresponding two props provided by `react-google-maps`.\n They're prefixed with _default_ (defaultUrl, defaultBounds).\n\n In some cases, you'll need the GroundOverlay component to reflect the changes\n of url and bounds. You can leverage the React's key property to remount the\n component. Typically, just `key={url}` would serve your need.\n See https://github.com/tomchentw/react-google-maps/issues/655\n"
)
var groundOverlay = new google.maps.GroundOverlay(
props.defaultUrl || props.url,
props.defaultBounds || props.bounds
)
;(0, _MapChildHelper.construct)(
GroundOverlay.propTypes,
updaterMap,
_this.props,
groundOverlay
)
groundOverlay.setMap(_this.context[_constants.MAP])
_this.state = (0, _defineProperty3.default)(
{},
_constants.GROUND_LAYER,
groundOverlay
)
return _this
}
;(0, _createClass3.default)(GroundOverlay, [
{
key: "componentDidMount",
value: function componentDidMount() {
;(0, _MapChildHelper.componentDidMount)(
this,
this.state[_constants.GROUND_LAYER],
eventMap
)
},
},
{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
;(0, _MapChildHelper.componentDidUpdate)(
this,
this.state[_constants.GROUND_LAYER],
eventMap,
updaterMap,
prevProps
)
},
},
{
key: "componentWillUnmount",
value: function componentWillUnmount() {
;(0, _MapChildHelper.componentWillUnmount)(this)
var GroundOverlay = this.state[_constants.GROUND_LAYER]
if (GroundOverlay) {
GroundOverlay.setMap(null)
}
},
},
{
key: "render",
value: function render() {
return false
},
/**
* Gets the `LatLngBounds` of this overlay.
* @type LatLngBoundsLatLngBounds
* @public
*/
},
{
key: "getBounds",
value: function getBounds() {
return this.state[_constants.GROUND_LAYER].getBounds()
},
/**
* Returns the opacity of this ground overlay.
* @type number
* @public
*/
},
{
key: "getOpacity",
value: function getOpacity() {
return this.state[_constants.GROUND_LAYER].getOpacity()
},
/**
* Gets the url of the projected image.
* @type string
* @public
*/
},
{
key: "getUrl",
value: function getUrl() {
return this.state[_constants.GROUND_LAYER].getUrl()
},
},
])
return GroundOverlay
})(
_react2.default.PureComponent
)) /*
then it seems like it has only a getOpacity()
method, but no setOpacity()
one. Also, the groundOverlay
variable is a local variable of the function GroundOverlay(props, context)
, so I don't see how I could access its setOpacity
method.
Any ideas on how to approach this?
I've noticed that the GroundOverlay
component accepts an opacity
prop which determines its opacity. Using this, I tried to create a component AdjustableGroundoverlay
containing both a <GroundOverlay>
and a <button>
(for now) to change the GroundOverlay
's opacity:
import React from "react"
import { MapWithGroundOverlay } from "./MapWithGroundOverlay"
export class AdjustableGroundoverlay extends React.PureComponent {
constructor(props, context) {
super(props, context)
this.state = {opacity: 0.5}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
opacity: 1.0
}));
}
render() {
return (
<div>
<MapWithGroundOverlay
googleMapURL={`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}&v=3.exp&libraries=geometry,drawing,places`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `600px` }} />}
mapElement={<div style={{ height: `100%` }} />}
opacity={this.state.opacity}
/>
<button onClick={this.handleClick}>
{`Opacity: ${this.state.opacity}`}
</button>
</div>
);
}
}
However, I've noticed that when I click the button, although its label changes from Opacity: 0.5
to Opacity: 1
, the actual opacity of the overlay doesn't change:
As I understand from https://reactjs.org/docs/handling-events.html, calling setState()
on the AdjustableGroundoverlay
should cause it to call its render()
method again, thereby passing the new this.state.opacity
as a prop to GroundOverlay
and changing its opacity. I don't understand why this is not working?
Upvotes: 1
Views: 1397
Reputation: 57421
Instead of using withStateHandlers
as in the answer above, I ended up just changing the defaultOpacity
prop to opacity
in the MapWithGroundOverlay
component:
/* global google */
import React from "react"
import { compose } from "recompose"
import {
withScriptjs,
withGoogleMap,
GoogleMap,
GroundOverlay } from "react-google-maps"
export const MapWithGroundOverlay = compose(
withScriptjs,
withGoogleMap
)(props =>
<GoogleMap
defaultZoom={12}
defaultCenter={{lat: 40.740, lng: -74.18}}
>
<GroundOverlay
defaultUrl="https://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg"
defaultBounds={new google.maps.LatLngBounds(
new google.maps.LatLng(40.712216, -74.22655),
new google.maps.LatLng(40.773941, -74.12544)
)}
opacity={props.opacity}
/>
</GoogleMap>
);
Now, using the AjustableGroundoverlay
component posted in the update above, the opacity changes when the button is clicked:
Upvotes: 0
Reputation: 59328
In react-google-maps
library default
properties (e.g. defaultOpacity
)are immutable after the component is instantiated, so in order GroundOverlay
component to reflect the changes, opacity
needs to be utilized instead.
Now comes the turn of changes, instead of accessing underlying google.maps.GroundOverlay
object, i would suggest a React way via state. Since in your example recompose
library is utilized opacity
state could be introduced and a handler to change its value could look like this:
withStateHandlers(
() => ({
opacity: 0.1
}),
{
incrementOpacity: ({ opacity }) => () => ({
opacity: opacity >= 1.0 ? 0.0 : opacity + 0.1
})
}
)
For details about
withStateHandlers
refer official documentation
Here is an example to reflect the change of opacity 'dynamically' (for demonstration purposes button is used instead of slider):
export const MapWithGroundOverlay = compose(
withScriptjs,
withGoogleMap,
withStateHandlers(
() => ({
opacity: 0.1
}),
{
incrementOpacity: ({ opacity }) => () => ({
opacity: opacity >= 1.0 ? 0.0 : opacity + 0.1
})
}
)
)(props => (
<div>
<button onClick={props.incrementOpacity}>Increment opacity</button>
<GoogleMap defaultZoom={12} defaultCenter={{ lat: 40.74, lng: -74.18 }}>
<GroundOverlay
defaultUrl="https://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg"
defaultBounds={
new google.maps.LatLngBounds(
new google.maps.LatLng(40.712216, -74.22655),
new google.maps.LatLng(40.773941, -74.12544)
)
}
defaultOpacity={props.opacity}
/>
</GoogleMap>
</div>
));
Upvotes: 2