├── .gitignore ├── README.md ├── app ├── AppDispatcher.js ├── actions │ └── ExampleActions.es6.js ├── components │ ├── Application.jsx │ └── SubComponent.jsx ├── constants │ └── ExampleConstants.js └── stores │ ├── BaseStore.es6.js │ └── ExampleStore.es6.js ├── package.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.orig 2 | 3 | # Mac Finder files 4 | .DS_Store 5 | ._.DS_Store 6 | 7 | # vim 8 | *.swp 9 | 10 | # Dependency detritus 11 | node_modules 12 | 13 | # WebStorm 14 | .idea/ 15 | 16 | # Compiled webpack bundles 17 | bundle.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | To Start: 2 | ``` 3 | npm install 4 | npm start 5 | open localhost:8080/webpack-dev-server/bundle 6 | ``` -------------------------------------------------------------------------------- /app/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | var Dispatcher = require('flux/lib/Dispatcher'); 2 | 3 | var copyProperties = require('react/lib/copyProperties'); 4 | 5 | var AppDispatcher = copyProperties(new Dispatcher(), { 6 | 7 | /** 8 | * A bridge function between the views and the dispatcher, marking the action 9 | * as a view action. Another variant here could be handleServerAction. 10 | * @param {object} action The data coming from the view. 11 | */ 12 | handleViewAction: function(action) { 13 | this.dispatch({ 14 | source: 'VIEW_ACTION', 15 | action: action 16 | }); 17 | } 18 | 19 | }); 20 | 21 | module.exports = AppDispatcher; -------------------------------------------------------------------------------- /app/actions/ExampleActions.es6.js: -------------------------------------------------------------------------------- 1 | var AppDispatcher = require('../AppDispatcher'); 2 | var ExampleConstants = require('constants/ExampleConstants'); 3 | 4 | var ExampleActions = { 5 | 6 | sendMessage : message => { 7 | AppDispatcher.handleViewAction({ 8 | actionType : ExampleConstants.SEND_MESSAGE, 9 | message : message 10 | }); 11 | } 12 | 13 | }; 14 | 15 | module.exports = ExampleActions; -------------------------------------------------------------------------------- /app/components/Application.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var ExampleStore = require('stores/ExampleStore'); 4 | var ExampleActions = require('actions/ExampleActions'); 5 | 6 | var SubComponent = require('./SubComponent'); 7 | 8 | var getState = function() { 9 | return { 10 | message : ExampleStore.message 11 | }; 12 | }; 13 | 14 | var counter = 0; 15 | 16 | var Application = React.createClass({ 17 | 18 | getInitialState : function() { 19 | return getState(); 20 | }, 21 | 22 | componentDidMount : function() { 23 | ExampleStore.addChangeListener(this._onChange); 24 | }, 25 | 26 | componentWillUnmount : function() { 27 | ExampleStore.removeChangeListener(this._onChange); 28 | }, 29 | 30 | _onChange : function() { 31 | this.setState(getState()); 32 | }, 33 | 34 | _sendMessage : function() { 35 | ExampleActions.sendMessage('hello world! ' + counter++); 36 | }, 37 | 38 | render : function() { 39 | return ( 40 |
41 |

Hello World!

42 |

You can edit stuff in here and it will hot update

43 | 44 | 45 | 46 |
47 | ); 48 | } 49 | 50 | }); 51 | 52 | React.renderComponent(Application(), document.body); -------------------------------------------------------------------------------- /app/components/SubComponent.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var SubComponent = React.createClass({ 4 | 5 | render : function() { 6 | return ( 7 |
8 |

SUB COMPONENT!

9 |

Message : {this.props.message}

10 |
11 | ); 12 | } 13 | 14 | }); 15 | 16 | module.exports = SubComponent; -------------------------------------------------------------------------------- /app/constants/ExampleConstants.js: -------------------------------------------------------------------------------- 1 | var keyMirror = require('react/lib/keyMirror'); 2 | 3 | module.exports = keyMirror({ 4 | SEND_MESSAGE : null 5 | }); -------------------------------------------------------------------------------- /app/stores/BaseStore.es6.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | 3 | var AppDispatcher = require('../AppDispatcher'); 4 | var EventEmitter = require('events').EventEmitter; 5 | var copyProperties = require('react/lib/copyProperties'); 6 | 7 | var CHANGE_EVENT = 'change'; 8 | 9 | 10 | class BaseStore { 11 | 12 | constructor(actions) { 13 | if (!_.isObject(actions)) { 14 | return; 15 | } 16 | 17 | // Register to handle all updates 18 | AppDispatcher.register(function(payload) { 19 | var action = payload.action; 20 | var text; 21 | 22 | if (actions[action.actionType]) { 23 | actions[action.actionType](action); 24 | } 25 | 26 | return true; // No errors. Needed by promise in Dispatcher. 27 | }); 28 | } 29 | 30 | emitChange() { 31 | this.emit(CHANGE_EVENT); 32 | } 33 | 34 | /** 35 | * @param {function} callback 36 | */ 37 | addChangeListener(callback) { 38 | this.on(CHANGE_EVENT, callback); 39 | } 40 | 41 | /** 42 | * @param {function} callback 43 | */ 44 | removeChangeListener(callback) { 45 | this.removeListener(CHANGE_EVENT, callback); 46 | } 47 | } 48 | 49 | copyProperties(BaseStore.prototype, EventEmitter.prototype); 50 | 51 | 52 | module.exports = BaseStore; -------------------------------------------------------------------------------- /app/stores/ExampleStore.es6.js: -------------------------------------------------------------------------------- 1 | var BaseStore = require('./BaseStore'); 2 | var ExampleConstants = require('constants/ExampleConstants'); 3 | 4 | var storeInstance; 5 | 6 | var message = ''; 7 | 8 | class ExampleStore extends BaseStore { 9 | get message() { 10 | return message; 11 | } 12 | } 13 | 14 | var actions = {}; 15 | 16 | actions[ExampleConstants.SEND_MESSAGE] = action => { 17 | message = action.message; 18 | storeInstance.emitChange(); 19 | }; 20 | 21 | storeInstance = new ExampleStore(actions); 22 | 23 | module.exports = storeInstance; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-workshop-boilerplate", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "start": "node node_modules/webpack-dev-server/bin/webpack-dev-server.js --hot" 6 | }, 7 | "devDependencies": { 8 | "es6-loader": "^0.2.0", 9 | "jsx-loader": "^0.11.0", 10 | "webpack": "^1.3.2-beta9", 11 | "webpack-dev-middleware": "^1.0.10", 12 | "webpack-dev-server": "~1.4.7", 13 | "less": "~1.7.5", 14 | "less-loader": "~0.7.7", 15 | "style-loader": "~0.8.0", 16 | "css-loader": "~0.9.0", 17 | "file-loader": "~0.7.2", 18 | "autoprefixer-loader": "~1.0.0", 19 | "react-hot-loader": "^0.4.5" 20 | }, 21 | "dependencies": { 22 | "flux": "^2.0.0", 23 | "react": "^0.11.1", 24 | "lodash": "^2.4.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | context : __dirname, 5 | entry : [ 6 | 'webpack/hot/dev-server', 7 | './app/components/Application' 8 | ], 9 | output : { 10 | path: __dirname, 11 | filename: 'bundle.js' 12 | }, 13 | plugins: [ 14 | new webpack.HotModuleReplacementPlugin(), 15 | new webpack.NoErrorsPlugin() 16 | ], 17 | module: { 18 | loaders: [ 19 | {test: /\.jsx$/, loaders: ['react-hot', 'jsx-loader?harmony&insertPragma=React.DOM']}, 20 | {test: /\.es6\.js$/, loader: 'es6-loader'}, 21 | 22 | // compile and include less files 23 | {test: /\.less$/, loader: 'style-loader!css-loader!autoprefixer-loader!less-loader'}, 24 | 25 | // allow less files to load urls pointing to font assets 26 | // @TODO: figure out why this is necessary and do it better 27 | {test: /\.(woff|ttf|eot|svg)$/, loader: 'file-loader' } 28 | ] 29 | }, 30 | resolve : { 31 | extensions: ['', '.js', '.es6.js', '.jsx'], 32 | alias : { 33 | actions : __dirname + '/app/actions', 34 | constants : __dirname + '/app/constants', 35 | stores : __dirname + '/app/stores' 36 | } 37 | } 38 | }; --------------------------------------------------------------------------------