frontend/src/loader/index.js
2019-04-03 19:47:26 +08:00

132 lines
2.8 KiB
JavaScript

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import hoistStatics from 'hoist-non-react-statics'
import { newScript, series, noop } from './utils'
const loadedScript = []
const pendingScripts = {}
let failedScript = []
export function startLoadingScripts(scripts, onComplete = noop) {
// sequence load
const loadNewScript = (script) => {
const src = typeof script === 'object' ? script.src : script
if (loadedScript.indexOf(src) < 0) {
return taskComplete => {
const callbacks = pendingScripts[src] || []
callbacks.push(taskComplete)
pendingScripts[src] = callbacks
if (callbacks.length === 1) {
return newScript(script)(err => {
pendingScripts[src].forEach(cb => cb(err, src))
delete pendingScripts[src]
})
}
}
}
}
const tasks = scripts.map(src => {
if (Array.isArray(src)) {
return src.map(loadNewScript)
}
else return loadNewScript(src)
})
series(...tasks)((err, src) => {
if (err) {
failedScript.push(src)
}
else {
if (Array.isArray(src)) {
src.forEach(addCache)
}
else addCache(src)
}
})(err => {
removeFailedScript()
onComplete(err)
})
}
const addCache = (entry) => {
if (loadedScript.indexOf(entry) < 0) {
loadedScript.push(entry)
}
}
const removeFailedScript = () => {
if (failedScript.length > 0) {
failedScript.forEach((script) => {
const node = document.querySelector(`script[src='${script}']`)
if (node != null) {
node.parentNode.removeChild(node)
}
})
failedScript = []
}
}
const scriptLoader = (...scripts) => (WrappedComponent) => {
class ScriptLoader extends Component {
static propTypes = {
onScriptLoaded: PropTypes.func
}
static defaultProps = {
onScriptLoaded: noop
}
constructor (props, context) {
super(props, context)
this.state = {
isScriptLoaded: false,
isScriptLoadSucceed: false
}
this._isMounted = false;
}
componentDidMount () {
this._isMounted = true;
startLoadingScripts(scripts, err => {
if(this._isMounted) {
this.setState({
isScriptLoaded: true,
isScriptLoadSucceed: !err
}, () => {
if (!err) {
this.props.onScriptLoaded()
}
})
}
})
}
componentWillUnmount () {
this._isMounted = false;
}
getWrappedInstance () {
return this.refs.wrappedInstance;
}
render () {
const props = {
...this.props,
...this.state,
ref: 'wrappedInstance'
}
return (
<WrappedComponent {...props} />
)
}
}
return hoistStatics(ScriptLoader, WrappedComponent)
}
export default scriptLoader