UNPKG

react-googlemaps

Version:

A declarative React interface to Google Maps

195 lines (160 loc) 5.36 kB
"use strict"; var assign = require('react/lib/Object.assign'); var invariant = require('react/lib/invariant'); var MapOption = require('./MapOption'); var MapOptionConfig = require('./MapOptionConfig'); var MapEvent = require('./MapEvent'); var GoogleMapsAPI = require('../GoogleMapsAPI'); /** * Cached reset map option object */ var resetMapOptionObject = {map: null}; /** * Empty props cache */ var emptyPropsCache = {}; /** * Empty function to be provided to event handlers */ function noop() {} /** * GoogleMaps React component mixin */ var ReactMapComponentMixin = { propTypes: assign({}, MapOptionConfig.Options), shouldComponentUpdate: function() { return this.__shouldComponentUpdate; }, componentDidMount: function() { this.__shouldComponentUpdate = true; this.__dirtyOptions = {}; this.__eventCache = {}; this.__node = this.constructGoogleMapsClass(); this._setInitialMapProperties(); this._updateMapProperties(emptyPropsCache); }, componentDidUpdate: function(prevProps) { this._updateMapProperties(prevProps); }, _setInitialMapProperties: function() { var initialMapOptions = {}; var props = this.props; for (var propKey in props) { var optionName = MapOption.getInitialOptionName[propKey]; if (!props.hasOwnProperty(propKey) || !optionName) { continue; } initialMapOptions[optionName] = props[propKey]; } this.flushOptionChanges(initialMapOptions); }, _updateMapProperties: function(lastProps) { var nextProps = this.props; var mapOptionChanges = {}; var mapEventChanges = {}; var propKey; // Remove any options or events that no longer // exist in the new set of props. for (propKey in lastProps) { if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey)) { continue; } if (MapEvent.isStandardName[propKey]) { mapEventChanges[propKey] = null; } else if (MapOption.isStandardName[propKey]) { mapOptionChanges[propKey] = null; } } // Add any changed options or new events. for (propKey in nextProps) { var nextProp = nextProps[propKey]; var lastProp = lastProps[propKey]; if (!nextProps.hasOwnProperty(propKey) || (nextProp === lastProp && !this.__dirtyOptions[propKey])) { continue; } if (MapEvent.isStandardName[propKey] && !lastProp) { mapEventChanges[propKey] = nextProp; } else if (MapOption.isStandardName[propKey]) { mapOptionChanges[MapOption.getOptionName[propKey]] = nextProps[propKey]; } } // Added check of changed options that have side effect events, // if they don't have a handler then add a noop one for this event // to trigger the side effect dirty checking. for (propKey in mapOptionChanges) { var sideEffectEvent = MapEvent.getOptionSideEffectEvent[propKey]; if (!mapOptionChanges.hasOwnProperty(propKey) || !sideEffectEvent || nextProps[sideEffectEvent]) { continue; } var hasNextProp = nextProps[propKey] != null; var hasLastProp = lastProps[propKey] != null; if (hasLastProp && !hasNextProp) { mapEventChanges[sideEffectEvent] = null; } else if (!hasLastProp && hasNextProp) { mapEventChanges[sideEffectEvent] = noop; } } this.flushOptionChanges(mapOptionChanges); this.flushEventChanges(mapEventChanges); }, flushOptionChanges: function(optionChanges) { MapEvent.setEnabled(false); this.__node.setOptions(optionChanges); MapEvent.setEnabled(true); }, flushEventChanges: function(eventChanges) { for (var eventName in eventChanges) { if (!eventChanges.hasOwnProperty(eventName)) { continue; } if (eventChanges[eventName]) { this.putListener(eventName); } else { this.deleteListener(eventName); } } }, componentWillUnmount: function() { this.deleteAllListeners(); if (this.props.map) { // If we still have a map prop at this point we should unset it from the node this.flushOptionChanges(resetMapOptionObject); } this.__node = null; }, putListener: function(eventName) { invariant(!this.__eventCache[eventName], 'Already has `%s` event bound', eventName); this.__eventCache[eventName] = GoogleMapsAPI.event.addListener( this.__node, MapEvent.getEventName[eventName], MapEvent.createEventDispatcher(eventName, this) ); }, deleteListener: function(eventName) { invariant(this.__eventCache[eventName], 'No event of `%s` bound to remove', eventName); GoogleMapsAPI.event.removeListener(this.__eventCache[eventName]); delete this.__eventCache[eventName]; }, deleteAllListeners: function() { for (var eventName in this.__eventCache) { if (this.__eventCache.hasOwnProperty(eventName)) { this.deleteListener(eventName); } } }, queueDirtyCheck: function() { this.__shouldComponentUpdate = false; }, flushDirtyChangesTo: function(effects) { this.__shouldComponentUpdate = true; this.__dirtyOptions[effects] = true; this.forceUpdate(); this.__dirtyOptions = {}; }, getMapNode: function() { return this.__node; } }; module.exports = ReactMapComponentMixin;