diff --git a/src/lib/async/ScriptjsGoogleMap.js b/src/lib/async/ScriptjsGoogleMap.js deleted file mode 100644 index c5d39627..00000000 --- a/src/lib/async/ScriptjsGoogleMap.js +++ /dev/null @@ -1,56 +0,0 @@ -import { - default as React, - Component, -} from "react"; - -import { - default as warning, -} from "warning"; - -import { - GoogleMap, -} from "../index"; - -import { - default as ScriptjsLoader, -} from "./ScriptjsLoader"; - -export default class ScriptjsGoogleMap extends Component { - - componentWillMount() { - warning(false, -`"async/ScriptjsGoogleMap" is deprecated now and will be removed in next major release (5.0.0). -Use "async/ScriptjsLoader" instead. -See https://github.com/tomchentw/react-google-maps/pull/150 for more details.` - ); - } - - render() { - const { - protocol, - hostname, - port, - pathname, - query, - loadingElement, - children, - ...restProps, - } = this.props; - - return ( - - {children} - - } - /> - ); - } -} diff --git a/src/lib/async/ScriptjsLoader.js b/src/lib/async/ScriptjsLoader.js deleted file mode 100644 index 37cf84da..00000000 --- a/src/lib/async/ScriptjsLoader.js +++ /dev/null @@ -1,130 +0,0 @@ -import _ from "lodash"; - -import { - default as React, - Component, - PropTypes, -} from "react"; - -import { - default as propTypesElementOfType, -} from "react-prop-types-element-of-type"; - -import { - default as canUseDOM, -} from "can-use-dom"; - -import { - default as warning, -} from "warning"; - -import { - GoogleMapLoader, - GoogleMap, -} from "../index"; - -import { - default as makeUrl, - urlObjDefinition, - getUrlObjChangedKeys, -} from "../utils/makeUrl"; - -const DELEGATE_PROPS_LIST = [ - `protocol`, - `hostname`, - `port`, - `pathname`, - `query`, - `loadingElement`, -]; - -export default class ScriptjsLoader extends Component { - static propTypes = { - ...urlObjDefinition, - // PropTypes for ScriptjsLoader - loadingElement: PropTypes.node, - // ...GoogleMapLoader.propTypes,// Uncomment for 5.0.0 - googleMapElement: propTypesElementOfType(GoogleMap).isRequired, - }; - - static defaultProps = { - }; - - state = { - isLoaded: false, - } - - setLoaded() { - this.setState({ isLoaded: true }); - } - - shouldUseNewBehavior() { - const { containerTagName, containerProps } = this.props.googleMapElement.props; - return ( - this.props.containerElement !== undefined && - this.props.containerElement !== null && - containerTagName === undefined && - containerProps === undefined - ); - } - - componentWillMount() { - warning(this.shouldUseNewBehavior(), -`"async/ScriptjsLoader" is now rendering "GoogleMapLoader". -Migrate to use "GoogleMapLoader" instead. -The old behavior will be removed in next major release (5.0.0). -See https://github.com/tomchentw/react-google-maps/pull/157 for more details.` - ); - if (!canUseDOM) { - return; - } - /* - * External commonjs require dependency -- begin - */ - const scriptjs = require(`scriptjs`); // eslint-disable-line global-require - /* - * External commonjs require dependency -- end - */ - const { protocol, hostname, port, pathname, query } = this.props; - const urlObj = { protocol, hostname, port, pathname, query }; - const url = makeUrl(urlObj); - scriptjs(url, () => this.setLoaded()); - } - - componentWillReceiveProps(nextProps) { - if (`production` !== process.env.NODE_ENV) { - const changedKeys = getUrlObjChangedKeys(this.props, nextProps); - - warning( - changedKeys.length === 0, -`ScriptjsLoader doesn't support mutating url related props after initial render. -Changed props: %s`, -`[${changedKeys.join(`, `)}]` - ); - } - } - - componentWillUnmount() { - // Set this to a no-op so we avoid using setState when the - // component is unmounted; - this.setLoaded = () => {}; - } - - render() { - if (this.state.isLoaded) { - if (this.shouldUseNewBehavior()) { - const nextProps = _.omit(this.props, DELEGATE_PROPS_LIST); - - return ( - - ); - } else { // ------------ Deprecated ------------ - return this.props.googleMapElement; - } - } else { - return this.props.loadingElement; - } - } -} diff --git a/src/lib/async/withScriptjs.js b/src/lib/async/withScriptjs.js new file mode 100644 index 00000000..b04dde93 --- /dev/null +++ b/src/lib/async/withScriptjs.js @@ -0,0 +1,98 @@ +import _ from "lodash"; + +import invariant from "invariant"; + +import canUseDOM from "can-use-dom"; + +import getDisplayName from "react-display-name"; + +import { + default as React, + PropTypes, + Component, +} from "react"; + +const LOAIDNG_STATE_NONE = `NONE`; +const LOAIDNG_STATE_BEGIN = `BEGIN`; +const LOAIDNG_STATE_LOADED = `LOADED`; + +export default function withScriptjs(WrappedComponent) { + return class Container extends Component { + static displayName = `withScriptjs(${getDisplayName(WrappedComponent)})`; + + static propTypes = { + loadingElement: PropTypes.node.isRequired, + googleMapURL: PropTypes.string.isRequired, + }; + + state = { + loadingState: LOAIDNG_STATE_NONE, + }; + + isUnmounted = false; + + handleLoaded = this.handleLoaded.bind(this); + + handleLoaded() { + if (this.isUnmounted) { + return; + } + this.setState({ + loadingState: LOAIDNG_STATE_LOADED, + }); + } + + componentWillMount() { + const { + loadingElement, + googleMapURL, + } = this.props; + invariant(!!loadingElement && !!googleMapURL, +`Required props loadingElement or googleMapURL is missing. You need to provide both of them.` + ); + } + + componentDidMount() { + const { + loadingState, + } = this.state; + if (loadingState !== LOAIDNG_STATE_NONE || !canUseDOM) { + return; + } + this.setState({ + loadingState: LOAIDNG_STATE_BEGIN, + }); + // Don't load scriptjs as dependency since we want this module be used on server side. + // eslint-disable-next-line global-require + const scriptjs = require(`scriptjs`); + const { + googleMapURL, + } = this.props; + scriptjs(googleMapURL, this.handleLoaded); + } + + componentWillUnmount() { + this.isUnmounted = true; + } + + render() { + const { + loadingElement, + googleMapURL, // eslint-disable-line no-unused-vars + ...restProps, + } = this.props; + + const { + loadingState, + } = this.state; + + if (loadingState === LOAIDNG_STATE_LOADED) { + return ( + + ); + } else { + return loadingElement; + } + } + }; +} diff --git a/src/lib/utils/makeUrl.js b/src/lib/utils/makeUrl.js deleted file mode 100644 index 062d7a6f..00000000 --- a/src/lib/utils/makeUrl.js +++ /dev/null @@ -1,34 +0,0 @@ -import _ from "lodash"; - -import { - format as formatUrlObj, -} from "url"; - -import { - PropTypes, -} from "react"; - -export default function makeUrl(urlObj) { - return formatUrlObj({ - protocol: urlObj.protocol, - hostname: urlObj.hostname, - port: urlObj.port, - pathname: urlObj.pathname, - query: urlObj.query, - }); -} - -export const urlObjDefinition = { - // PropTypes for URL generation - // https://nodejs.org/api/url.html#url_url_format_urlobj - protocol: PropTypes.string, - hostname: PropTypes.string.isRequired, - port: PropTypes.number, - pathname: PropTypes.string.isRequired, - query: PropTypes.object.isRequired, -}; - -export function getUrlObjChangedKeys(urlObj, nextUrlObj) { - return Object.keys(urlObjDefinition) - .filter(key => !_.isEqual(urlObj[key], nextUrlObj[key])); -}