Skip to content

feat: added withLoadjs wrapper to be able to handle script load failures #830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ Object.defineProperty(exports, "withScriptjs", {
},
})

var _withLoadjs = require("./withLoadjs")

Object.defineProperty(exports, "withLoadjs", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_withLoadjs).default
},
})

var _withGoogleMap = require("./withGoogleMap")

Object.defineProperty(exports, "withGoogleMap", {
Expand Down
209 changes: 209 additions & 0 deletions lib/withLoadjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
"use strict"

Object.defineProperty(exports, "__esModule", {
value: true,
})

var _objectWithoutProperties2 = require("babel-runtime/helpers/objectWithoutProperties")

var _objectWithoutProperties3 = _interopRequireDefault(
_objectWithoutProperties2
)

var _getPrototypeOf = require("babel-runtime/core-js/object/get-prototype-of")

var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf)

var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck")

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2)

var _createClass2 = require("babel-runtime/helpers/createClass")

var _createClass3 = _interopRequireDefault(_createClass2)

var _possibleConstructorReturn2 = require("babel-runtime/helpers/possibleConstructorReturn")

var _possibleConstructorReturn3 = _interopRequireDefault(
_possibleConstructorReturn2
)

var _inherits2 = require("babel-runtime/helpers/inherits")

var _inherits3 = _interopRequireDefault(_inherits2)

var _bind2 = require("lodash/bind")

var _bind3 = _interopRequireDefault(_bind2)

exports.withLoadjs = withLoadjs

var _invariant = require("invariant")

var _invariant2 = _interopRequireDefault(_invariant)

var _canUseDom = require("can-use-dom")

var _canUseDom2 = _interopRequireDefault(_canUseDom)

var _recompose = require("recompose")

var _propTypes = require("prop-types")

var _propTypes2 = _interopRequireDefault(_propTypes)

var _react = require("react")

var _react2 = _interopRequireDefault(_react)

function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj }
}

var LOADING_STATE_NONE = "NONE"
var LOADING_STATE_BEGIN = "BEGIN"
var LOADING_STATE_LOADED = "LOADED"
var LOADING_STATE_FAILED = "FAILED"

function withLoadjs(BaseComponent) {
var factory = _react2.default.createFactory(BaseComponent)

var Container = (function(_React$PureComponent) {
;(0, _inherits3.default)(Container, _React$PureComponent)

function Container() {
var _ref

var _temp, _this, _ret

;(0, _classCallCheck3.default)(this, Container)

for (
var _len = arguments.length, args = Array(_len), _key = 0;
_key < _len;
_key++
) {
args[_key] = arguments[_key]
}

return (
(_ret = ((_temp = ((_this = (0, _possibleConstructorReturn3.default)(
this,
(_ref =
Container.__proto__ ||
(0, _getPrototypeOf2.default)(Container)).call.apply(
_ref,
[this].concat(args)
)
)),
_this)),
(_this.state = {
loadingState: LOADING_STATE_NONE,
}),
(_this.isUnmounted = false),
(_this.handleLoaded = (0, _bind3.default)(_this.handleLoaded, _this)),
_temp)),
(0, _possibleConstructorReturn3.default)(_this, _ret)
)
}

;(0, _createClass3.default)(Container, [
{
key: "handleLoaded",
value: function handleLoaded(error) {
if (this.isUnmounted) {
return
}
this.setState({
loadingState: error ? LOADING_STATE_FAILED : LOADING_STATE_LOADED,
})
},
},
{
key: "componentWillMount",
value: function componentWillMount() {
var _props = this.props,
loadingElement = _props.loadingElement,
googleMapURL = _props.googleMapURL

;(0, _invariant2.default)(
!!loadingElement && !!googleMapURL,
"Required props loadingElement or googleMapURL is missing. You need to provide both of them."
)
},
},
{
key: "componentDidMount",
value: function componentDidMount() {
var _this2 = this

var loadingState = this.state.loadingState

if (loadingState !== LOADING_STATE_NONE || !_canUseDom2.default) {
return
}
this.setState({
loadingState: LOADING_STATE_BEGIN,
})
// Don't load scriptjs as a dependency since we do not want this module be used on server side.
// eslint-disable-next-line global-require
var loadjs = require("load-js")
var googleMapURL = this.props.googleMapURL

loadjs([
{
url: googleMapURL,
async: true,
},
])
.then(function() {
return _this2.handleLoaded()
})
.catch(this.handleLoaded)
},
},
{
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.isUnmounted = true
},
},
{
key: "render",
value: function render() {
var _props2 = this.props,
loadingElement = _props2.loadingElement,
failedElement = _props2.failedElement,
googleMapURL = _props2.googleMapURL,
restProps = (0, _objectWithoutProperties3.default)(_props2, [
"loadingElement",
"failedElement",
"googleMapURL",
])
var loadingState = this.state.loadingState

if (loadingState === LOADING_STATE_LOADED) {
return factory(restProps)
} else if (loadingState === LOADING_STATE_FAILED) {
return failedElement
} else {
return loadingElement
}
},
},
])
return Container
})(_react2.default.PureComponent)

Container.displayName =
"withLoadjs(" + (0, _recompose.getDisplayName)(BaseComponent) + ")"
Container.propTypes = {
loadingElement: _propTypes2.default.node.isRequired,
failedElement: _propTypes2.default.node.isRequired,
googleMapURL: _propTypes2.default.string.isRequired,
}

return Container
}

exports.default = withLoadjs
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"can-use-dom": "^0.1.0",
"google-maps-infobox": "^2.0.0",
"invariant": "^2.2.1",
"load-js": "^3.0.2",
"lodash": "^4.16.2",
"marker-clusterer-plus": "^2.1.4",
"markerwithlabel": "^2.0.1",
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export { default as withScriptjs } from "./withScriptjs"

export { default as withLoadjs } from "./withLoadjs"

export { default as withGoogleMap } from "./withGoogleMap"

export { default as GoogleMap } from "./components/GoogleMap"
Expand Down
99 changes: 99 additions & 0 deletions src/withLoadjs.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import _ from "lodash"
import invariant from "invariant"
import canUseDOM from "can-use-dom"
import { getDisplayName } from "recompose"
import PropTypes from "prop-types"
import React from "react"

const LOADING_STATE_NONE = `NONE`
const LOADING_STATE_BEGIN = `BEGIN`
const LOADING_STATE_LOADED = `LOADED`
const LOADING_STATE_FAILED = `FAILED`

export function withLoadjs(BaseComponent) {
const factory = React.createFactory(BaseComponent)

class Container extends React.PureComponent {
static displayName = `withLoadjs(${getDisplayName(BaseComponent)})`

static propTypes = {
loadingElement: PropTypes.node.isRequired,
failedElement: PropTypes.node.isRequired,
googleMapURL: PropTypes.string.isRequired,
}

state = {
loadingState: LOADING_STATE_NONE,
}

isUnmounted = false

handleLoaded = _.bind(this.handleLoaded, this)

handleLoaded(error) {
if (this.isUnmounted) {
return
}
this.setState({
loadingState: error ? LOADING_STATE_FAILED : LOADING_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 !== LOADING_STATE_NONE || !canUseDOM) {
return
}
this.setState({
loadingState: LOADING_STATE_BEGIN,
})
// Don't load scriptjs as a dependency since we do not want this module be used on server side.
// eslint-disable-next-line global-require
const loadjs = require(`load-js`)
const { googleMapURL } = this.props
loadjs([
{
url: googleMapURL,
async: true,
},
])
.then(() => this.handleLoaded())
.catch(this.handleLoaded)
}

componentWillUnmount() {
this.isUnmounted = true
}

render() {
const {
loadingElement,
failedElement,
googleMapURL, // eslint-disable-line no-unused-vars
...restProps
} = this.props

const { loadingState } = this.state

if (loadingState === LOADING_STATE_LOADED) {
return factory(restProps)
} else if (loadingState === LOADING_STATE_FAILED) {
return failedElement
} else {
return loadingElement
}
}
}

return Container
}

export default withLoadjs
35 changes: 35 additions & 0 deletions src/withLoadjs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
### Props

* googleMapURL: String
* loadingElement: ReactElement
* failedElement: ReactElement

### Usage

```jsx static
import {
withLoadjs,
withGoogleMap,
GoogleMap,
Marker,
} from "react-google-maps";

const MapWithAMarker = withLoadjs(withGoogleMap(props =>
<GoogleMap
defaultZoom={8}
defaultCenter={{ lat: -34.397, lng: 150.644 }}
>
<Marker
position={{ lat: -34.397, lng: 150.644 }}
/>
</GoogleMap>
));

<MapWithAMarker
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg&v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
failedElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
```
4 changes: 4 additions & 0 deletions styleguide.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ module.exports = {
name: "withScriptjs",
content: "src/withScriptjs.md",
},
{
name: "withLoadjs",
content: "src/withLoadjs.md",
},
],
},
{
Expand Down
Loading