├── .babelrc ├── .eslintrc ├── .gitignore ├── README.md ├── example ├── index.html ├── index.js └── script.js ├── lib └── index.js ├── package.json ├── server.js ├── src └── index.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "rules" :{ 4 | "quotes": [1, "single"], 5 | "no-unused-vars": [1, {"vars": "all", "args": "all"}], 6 | "strict": [2, "never"] 7 | }, 8 | "env":{ 9 | "browser": true, 10 | "node" : true 11 | } 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # osx noise 2 | .DS_Store 3 | profile 4 | 5 | # xcode noise 6 | build/* 7 | *.mode1 8 | *.mode1v3 9 | *.mode2v3 10 | *.perspective 11 | *.perspectivev3 12 | *.pbxuser 13 | *.xcworkspace 14 | xcuserdata 15 | 16 | # svn & cvs 17 | .svn 18 | CVS 19 | node_modules 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | react-loadscript 2 | --- 3 | 4 | `npm install react react-loadscript --save` 5 | 6 | script-loading as a component 7 | 8 | (I'm so sorry) 9 | 10 | ```js 11 | import {Script} from 'react-loadscript'; 12 | 13 | class App { 14 | render(){ 15 | return 20 | } 21 | } 22 | ``` 23 | 24 | uses promises + a simple cache to prevent duplicate script loads. enjoy! 25 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Script} from '../src'; 3 | 4 | class App { 5 | render(){ 6 | return 9 | } 10 | } 11 | 12 | React.render(, document.getElementById('root')); -------------------------------------------------------------------------------- /example/script.js: -------------------------------------------------------------------------------- 1 | console.log(123); -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 12 | 13 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 14 | 15 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 16 | 17 | var _react = require('react'); 18 | 19 | var _react2 = _interopRequireDefault(_react); 20 | 21 | var promises = {}; 22 | 23 | function loadScript(src) { 24 | if (promises[src]) { 25 | return promises[src]; 26 | } 27 | var promise = promises[src] = new Promise(function (resolve) { 28 | var el = document.createElement('script'); 29 | var loaded = false; 30 | el.onload = el.onreadystatechange = function () { 31 | if (el.readyState && el.readyState !== 'complete' && el.readyState !== 'loaded' || loaded) { 32 | return false; 33 | } 34 | el.onload = el.onreadystatechange = null; 35 | loaded = true; 36 | resolve(); 37 | }; 38 | 39 | el.async = true; 40 | el.src = src; 41 | var head = document.getElementsByTagName('head')[0]; 42 | head.insertBefore(el, head.firstChild); 43 | }); 44 | 45 | return promise; 46 | } 47 | 48 | var Script = (function (_React$Component) { 49 | _inherits(Script, _React$Component); 50 | 51 | function Script() { 52 | _classCallCheck(this, Script); 53 | 54 | _get(Object.getPrototypeOf(Script.prototype), 'constructor', this).apply(this, arguments); 55 | 56 | this.state = { 57 | done: false 58 | }; 59 | } 60 | 61 | _createClass(Script, [{ 62 | key: 'componentWillMount', 63 | value: function componentWillMount() { 64 | var _this = this; 65 | 66 | loadScript(this.props.src).then(function () { 67 | _this.setState({ 68 | done: true 69 | }); 70 | _this.props.onLoad(); 71 | }); 72 | } 73 | }, { 74 | key: 'render', 75 | value: function render() { 76 | return this.props.children(this.state); 77 | } 78 | }], [{ 79 | key: 'defaultProps', 80 | value: { 81 | src: 'javascript:void(0)', 82 | onLoad: function onLoad() {} 83 | }, 84 | enumerable: true 85 | }, { 86 | key: 'propTypes', 87 | value: { 88 | children: _react2['default'].PropTypes.func, 89 | src: _react2['default'].PropTypes.string, 90 | onLoad: _react2['default'].PropTypes.func 91 | }, 92 | enumerable: true 93 | }]); 94 | 95 | return Script; 96 | })(_react2['default'].Component); 97 | 98 | exports.Script = Script; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-loadscript", 3 | "version": "1.0.2", 4 | "description": "script tag as a react component", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "start": "babel-node server.js", 8 | "build": "babel src -d lib", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/threepointone/react-script.git" 14 | }, 15 | "author": "Sunil Pai ", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/threepointone/react-script/issues" 19 | }, 20 | "homepage": "https://github.com/threepointone/react-script#readme", 21 | "devDependencies": { 22 | "babel": "^5.8.21", 23 | "babel-core": "^5.8.22", 24 | "babel-eslint": "^4.0.5", 25 | "babel-loader": "^5.3.2", 26 | "react": "^0.13.3", 27 | "webpack": "^1.11.0", 28 | "webpack-dev-server": "^1.10.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import WebpackDevServer from 'webpack-dev-server'; 3 | import config from './webpack.config'; 4 | 5 | const isHot = !!process.env.HOT; 6 | 7 | new WebpackDevServer(webpack(config), { 8 | publicPath: config.output.publicPath, 9 | hot: isHot, 10 | historyApiFallback: true 11 | }).listen(3000, 'localhost', err => console.log(err || 'webpack at localhost:3000')); 12 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | const promises = {}; 3 | 4 | function loadScript(src){ 5 | if(promises[src]){ 6 | return promises[src] 7 | } 8 | let promise = promises[src] = new Promise(resolve => { 9 | var el = document.createElement('script'); 10 | var loaded = false; 11 | el.onload = el.onreadystatechange = () => { 12 | if ((el.readyState && el.readyState !== 'complete' && el.readyState !== 'loaded') || loaded) { 13 | return false; 14 | } 15 | el.onload = el.onreadystatechange = null; 16 | loaded = true; 17 | resolve(); 18 | }; 19 | 20 | el.async = true; 21 | el.src = src; 22 | let head = document.getElementsByTagName('head')[0]; 23 | head.insertBefore(el, head.firstChild); 24 | 25 | }); 26 | 27 | return promise; 28 | } 29 | 30 | export class Script extends React.Component{ 31 | static defaultProps = { 32 | src: 'javascript:void(0)', 33 | onLoad: () => {} 34 | } 35 | static propTypes = { 36 | children: React.PropTypes.func, 37 | src: React.PropTypes.string, 38 | onLoad: React.PropTypes.func 39 | } 40 | state = { 41 | done: false 42 | } 43 | componentWillMount() { 44 | loadScript(this.props.src).then(()=> { 45 | this.setState({ 46 | done: true 47 | }); 48 | this.props.onLoad(); 49 | }); 50 | } 51 | render(){ 52 | return this.props.children(this.state); 53 | } 54 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | 3 | let config = { 4 | devtool: 'source-map', 5 | target: 'web', 6 | entry: { 7 | example: ['./example/index.js'] 8 | 9 | }, 10 | output: { 11 | path: __dirname, 12 | filename: 'example/bundle.js', 13 | publicPath: '/' 14 | }, 15 | module: { 16 | loaders: [{ 17 | test: /\.js$/, 18 | exclude: /node_modules/, 19 | loaders: ['babel-loader'] 20 | }] 21 | }, 22 | resolve: { 23 | extensions: ['', '.js', '.jsx'] 24 | }, 25 | plugins: [] 26 | }; 27 | 28 | if(process.env.HOT){ 29 | config = { 30 | ...config, 31 | devtool: 'eval-source-map', 32 | entry: Object.keys(config.entry).reduce((o, key) => ({...o, [key]: [ 33 | 'webpack-dev-server/client?http://localhost:3000', // WebpackDevServer host and port 34 | 'webpack/hot/only-dev-server' 35 | ].concat(config.entry[key])}), {}), 36 | module: {...config.module, 37 | loaders: [{ 38 | ...config.module.loaders[0], 39 | loaders: [ 40 | 'react-hot' 41 | ].concat(config.module.loaders[0].loaders) 42 | }] 43 | }, 44 | plugins: [ 45 | new webpack.HotModuleReplacementPlugin(), 46 | new webpack.NoErrorsPlugin() 47 | ].concat(config.plugins) 48 | }; 49 | } 50 | 51 | module.exports = config; 52 | --------------------------------------------------------------------------------