Created
June 27, 2015 17:35
-
-
Save daimagine/fd56249ef18c1b454830 to your computer and use it in GitHub Desktop.
React mixin with script loader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react/addons'; | |
import ReactMixin from 'react-mixin'; | |
import ReactScriptLoaderMixin from 'react-script-loader'; | |
export default class LoginForm extends React.Component { | |
constructor() { | |
super(); | |
this.state = { | |
user: '', | |
password: '', | |
scriptLoading: true, | |
scriptLoadError: false, | |
}; | |
} | |
// this function tells ReactScriptLoaderMixin where to load the script from | |
getScriptURL() { | |
return 'https://www.google.com/recaptcha/api.js'; | |
} | |
// ReactScriptLoaderMixin calls this function when the script has loaded | |
// successfully. | |
onScriptLoaded() { | |
console.log('ReactScriptLoaderMixin: onScriptLoaded'); | |
this.setState({scriptLoading: false}); | |
} | |
// ReactScriptLoaderMixin calls this function when the script has failed to load. | |
onScriptError() { | |
console.log('ReactScriptLoaderMixin: onScriptLoaded'); | |
this.setState({scriptLoading: false, scriptLoadError: true}); | |
} | |
onScriptTagCreated() { | |
console.log('ReactScriptLoaderMixin: onScriptTagCreated'); | |
} | |
login(e) { | |
e.preventDefault(); | |
alert('will login'); | |
} | |
render() { | |
var message = "react script loader: " | |
if (this.state.scriptLoading) { | |
message += 'loading script...'; | |
} else if (this.state.scriptLoadError) { | |
message += 'loading failed'; | |
} else { | |
message += 'loading succeeded'; | |
} | |
console.log(message); | |
return( | |
<form role="form" id="login-form" className="login-form"> | |
<div className="row"> | |
<div className="form-group col-md-12"> | |
<label className="form-label">Username</label> | |
<div className="controls"> | |
<div className="input-with-icon right"> | |
<i></i> | |
<input id="txtusername" type="text" name="txtusername" className="form-control" | |
valueLink={this.linkState('user')} /> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div className="row"> | |
<div className="form-group col-md-12"> | |
<label className="form-label">Password</label><span className="help"></span> | |
<div className="controls"> | |
<div className="input-with-icon right"><i></i> | |
<input id="txtpassword" type="password" name="txtpassword" className="form-control" | |
valueLink={this.linkState('password')} /> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div className="row"> | |
<div className="form-group col-md-12"> | |
<label className="form-label">Captcha</label><span className="help"></span> | |
<div className="controls"> | |
<div className="input-with-icon right"><i></i> | |
<div className="g-recaptcha" data-sitekey="6Lc4zggTAAAAAIQhg0kFlMA0qpy4PLyulMaPt-8-"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div className="row"> | |
<div className="control-group col-md-12"> | |
<div className="checkbox checkbox check-success"><a href="#">Trouble login in?</a> | |
<br/><br/> | |
<input id="checkbox1" type="checkbox" value="1" /> | |
<label htmlFor="checkbox1">Keep me reminded</label> | |
</div> | |
</div> | |
</div> | |
<div className="row"> | |
<div className="col-md-12"> | |
<button type="submit" className="btn btn-primary btn-cons pull-right" | |
onClick={this.login.bind(this)}>Login</button> | |
</div> | |
</div> | |
</form> | |
); | |
} | |
} | |
ReactMixin(LoginForm.prototype, React.addons.LinkedStateMixin); | |
ReactMixin(LoginForm.prototype, ReactScriptLoaderMixin); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A dictionary mapping script URLs to a dictionary mapping | |
// component key to component for all components that are waiting | |
// for the script to load. | |
var scriptObservers = {}; | |
// A dictionary mapping script URL to a boolean value indicating if the script | |
// has already been loaded. | |
var loadedScripts = {}; | |
// A dictionary mapping script URL to a boolean value indicating if the script | |
// has failed to load. | |
var erroredScripts = {}; | |
// A counter used to generate a unique id for each component that uses | |
// ScriptLoaderMixin. | |
var idCount = 0; | |
var ReactScriptLoader = { | |
componentDidMount: function(key, component, scriptURL) { | |
if (typeof component.onScriptLoaded !== 'function') { | |
throw new Error('ScriptLoader: Component class must implement onScriptLoaded()'); | |
} | |
if (typeof component.onScriptError !== 'function') { | |
throw new Error('ScriptLoader: Component class must implement onScriptError()'); | |
} | |
if (loadedScripts[scriptURL]) { | |
component.onScriptLoaded(); | |
return; | |
} | |
if (erroredScripts[scriptURL]) { | |
component.onScriptError(); | |
return; | |
} | |
// If the script is loading, add the component to the script's observers | |
// and return. Otherwise, initialize the script's observers with the component | |
// and start loading the script. | |
if (scriptObservers[scriptURL]) { | |
console.log('ReactScriptLoaderMixin: add component to scriptObservers'); | |
scriptObservers[scriptURL][key] = component; | |
return; | |
} | |
var observers = {}; | |
observers[key] = component; | |
scriptObservers[scriptURL] = observers; | |
console.log('ReactScriptLoaderMixin: creating script tag'); | |
var script = document.createElement('script'); | |
if (typeof component.onScriptTagCreated === 'function') { | |
component.onScriptTagCreated(script); | |
} | |
script.src = scriptURL; | |
script.async = 1; | |
var callObserverFuncAndRemoveObserver = function(func) { | |
var observers = scriptObservers[scriptURL]; | |
for (var key in observers) { | |
observer = observers[key]; | |
var removeObserver = func(observer); | |
if (removeObserver) { | |
delete scriptObservers[scriptURL][key]; | |
} | |
} | |
//delete scriptObservers[scriptURL]; | |
} | |
script.onload = function() { | |
loadedScripts[scriptURL] = true; | |
callObserverFuncAndRemoveObserver(function(observer) { | |
if (observer.deferOnScriptLoaded && observer.deferOnScriptLoaded()) { | |
return false; | |
} | |
observer.onScriptLoaded(); | |
return true; | |
}); | |
}; | |
script.onerror = function(event) { | |
erroredScripts[scriptURL] = true; | |
callObserverFuncAndRemoveObserver(function(observer) { | |
observer.onScriptError(); | |
return true; | |
}); | |
}; | |
// (old) MSIE browsers may call 'onreadystatechange' instead of 'onload' | |
script.onreadystatechange = function() { | |
if (this.readyState == 'loaded') { | |
// wait for other events, then call onload if default onload hadn't been called | |
window.setTimeout(function() { | |
if (loadedScripts[scriptURL] !== true) script.onload(); | |
}, 0); | |
} | |
}; | |
console.log('append script', script); | |
document.body.appendChild(script); | |
}, | |
componentWillUnmount: function(key, scriptURL) { | |
// If the component is waiting for the script to load, remove the | |
// component from the script's observers before unmounting the component. | |
var observers = scriptObservers[scriptURL]; | |
if (observers) { | |
delete observers[key]; | |
} | |
}, | |
triggerOnScriptLoaded: function(scriptURL) { | |
if (!loadedScripts[scriptURL]) { | |
throw new Error('Error: only call this function after the script has in fact loaded.'); | |
} | |
var observers = scriptObservers[scriptURL]; | |
for (var key in observers) { | |
var observer = observers[key]; | |
observer.onScriptLoaded(); | |
} | |
delete scriptObservers[scriptURL]; | |
} | |
}; | |
var ReactScriptLoaderMixin = { | |
componentDidMount: function() { | |
if (typeof this.getScriptURL !== 'function') { | |
throw new Error("ScriptLoaderMixin: Component class must implement getScriptURL().") | |
} | |
console.log('ReactScriptLoaderMixin: componentDidMount'); | |
if (this.getScriptURL() instanceof Array) { | |
console.log('ReactScriptLoaderMixin: load array of scripts', this.getScriptURL()); | |
for (var i in this.getScriptURL()) { | |
ReactScriptLoader.componentDidMount(this.__getScriptLoaderID(), this, this.getScriptURL()[i]); | |
} | |
} else { | |
ReactScriptLoader.componentDidMount(this.__getScriptLoaderID(), this, this.getScriptURL()); | |
} | |
}, | |
componentWillUnmount: function() { | |
console.log('ReactScriptLoaderMixin: componentWillUnmount'); | |
if (this.getScriptURL() instanceof Array) { | |
for (var i in this.getScriptURL()) { | |
ReactScriptLoader.componentWillUnmount(this.__getScriptLoaderID(), this, this.getScriptURL()[i]); | |
} | |
} else { | |
ReactScriptLoader.componentWillUnmount(this.__getScriptLoaderID(), this.getScriptURL()); | |
} | |
}, | |
__getScriptLoaderID: function() { | |
if (typeof this.__reactScriptLoaderID === 'undefined') { | |
this.__reactScriptLoaderID = 'id' + idCount++; | |
} | |
return this.__reactScriptLoaderID; | |
}, | |
}; | |
exports.ReactScriptLoaderMixin = ReactScriptLoaderMixin; | |
exports.ReactScriptLoader = ReactScriptLoader; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment