11_Workoutlogger ---ReactJS and Flux: Learn By Building 10 Projects

来源:互联网 发布:马小丝的淘宝店 编辑:程序博客网 时间:2024/06/05 09:09
学习编程的最好方法就是去编程。

这篇笔记项目来自于:
https://www.udemy.com/reactjs-and-flux-learn-by-building-10-projects/ 的第11课。

最终效果如图:
workoutLogger App

这个项目是单页面应用,使用的是客户端的渲染,没有用到服务器。
package.json

{  "name": "workoutlogger",  "version": "1.0.0",  "description": "Workout Logger",  "main": "index.js",  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1"  },  "author": "Morphues Cao",  "license": "ISC",  "devDependencies":{    "browserify":"*",    "gulp":"*",    "reactify":"*",    "vinyl-source-stream":"*",    "object-assign":"*"  },  "dependencies":{    "react":"^0.14.7",    "react-dom":"^0.14.7",    "flux":"^2.1.1"  }}

使用gulp进行编译,复制和监控。gulpfile.js

//path %PATH%;.\node_modules\.binvar gulp = require('gulp');var browserify = require('browserify');var reactify = require('reactify');var source = require('vinyl-source-stream');gulp.task('browserify',function(){    browserify('./src/js/main.js')        .transform('reactify')      //jsx -> js        .bundle()        .pipe(source('main.js'))        .pipe(gulp.dest('dist/js'));});gulp.task('copy',function(){    gulp.src('src/index.html')        .pipe(gulp.dest('dist'));    gulp.src('src/css/*.*')        .pipe(gulp.dest('dist/css'));    gulp.src('src/js/vendors/*.*')        .pipe(gulp.dest('dist/js'));});gulp.task('default',['browserify','copy'],function(){    return gulp.watch('src/**/*.*',['browserify','copy']);});

目录结构:
项目目录树
参考flux的unidirectional data flow.:
这里写图片描述
对应的文件结构就更容易理解:
index.html

<html>    <head>        <title>Workout logger</title>        <link rel="stylesheet" href="css/bootstrap.css"/>        <link rel="stylesheet" href="css/style.css"/>    </head>    <body>        <div class="row">            <div class="col-md-4 col-md-offset-4">                <div id="app">TEST APP</div>            </div>        </div>        <script src="js/jquery.js"></script>        <script src="js/main.js"></script>    </body></html>

js/main.js

var App = require('./components/App');var React = require('react');var ReactDOM = require('react-dom');var AppAPI = require('./utils/AppAPI.js')var StartData = require('./startData.js');if(localStorage.getItem('workouts') == null){    StartData.init();}AppAPI.getWorkouts();ReactDOM.render(    <App />,    document.getElementById('app'));

js/components/App.js

var React = require('react');var AppActions = require('../actions/AppActions');var AppStore = require('../stores/AppStore');var AddForm = require('./AddForm.js');var Workouts = require('./Workouts.js');function getAppState(){    return {        showForm: AppStore.getShowForm(),        workouts: AppStore.getWorkouts()    }}var App = React.createClass({    getInitialState: function(){        return getAppState();    },    componentDidMount:function(){        AppStore.addChangeListener(this._onChange);    },    componentWillUnmount:function(){        AppStore.removeChangeListener(this._onChange)    },    onShowFormClick:function(e){        e.preventDefault();        AppActions.showForm();    },    render: function(){        console.log(this.state.workouts)        if(this.state.showForm){            var form = <AddForm />        }else{             var form = '';        }        return (            <div>                <h1 className="text-center page-header">WorkoutLogger</h1>                <a onClick={this.onShowFormClick} href="#" className="btn btn-primary btn-block">Add Workout</a>                <br/>                {form}                <br/>                <Workouts workouts = {this.state.workouts} />                <br/>            </div>        )    },    _onChange: function(){        this.setState(getAppState());    }});module.exports = App;

js/components/AddForm.js

var React = require('react');var AppActions = require('../actions/AppActions');var AppStore = require('../stores/AppStore');var AddForm = React.createClass({    render: function(){        return (            <form onSubmit={this.onSubmit}>                <div className="form-group">                    <select className="form-control" ref="type">                        <option value="Jogging">Jogging</option>                        <option value="Weight Lifting">Weight Lifting</option>                        <option value="Elliptical">Elliptical</option>                        <option value="Yoga">Yoga</option>                        <option value="Other">Other</option>                    </select>                </div>                <div className="form-group">                        <input type="text" className="form-control"                        ref="minutes" placeholder="Minutes"/>                </div>                <div className="form-group">                        <input type="text" className="form-control"                        ref="miles" placeholder="Miles(Optional)"/>                </div>                <button type="submit" className="btn btn-default btn-block">Log Workout</button>            </form>        )    },    onSubmit: function(e){        e.preventDefault();        var workout = {            id: this.generateId(),            type:this.refs.type.value.trim(),            minutes:this.refs.minutes.value.trim(),            miles:this.refs.miles.value.trim(),            date:new Date()        }        //console.log(workout);        AppActions.addWorkout(workout);    },    generateId: function(){        var id='';        var possible = '0123456789';        for(var i=0;i<5;i++){            id += possible.charAt(Math.floor(Math.random() * possible.length));        }        return id;    }});module.exports = AddForm;

js/components/Workouts.js

var React = require('react');var AppActions = require('../actions/AppActions');var AppStore = require('../stores/AppStore');var Workout = require('./Workout');var Workouts = React.createClass({    render: function(){        return (            <ul className="list-group">                {                    this.props.workouts.map(function(workout,i){                        // console.log(workout);                        return(                            <Workout workout={workout} key={i} />                        )                    })                }            </ul>        )    }});module.exports = Workouts;

js/components/Workout.js

var React = require('react');var AppActions = require('../actions/AppActions');var AppStore = require('../stores/AppStore');var Workout = React.createClass({    render: function(){        if(this.props.workout.miles != ''){            var miles = ' | '+this.props.workout.miles + 'Miles';        }else{            var miles = '';        }        return (            <li className="list-group-item">                 {this.props.workout.type} - {this.props.workout.minutes} Minutes {miles}                    <a href="#" onClick={this.onDelete.bind(this,this.props.workout.id)}                     className="delete">X</a>            </li>        )    },    onDelete:function(i,j){        AppActions.removeWorkout(i);    }});module.exports = Workout;

js/actions/AppActions.js

var AppDispatcher = require('../dispatcher/AppDispatcher');var AppConstants = require('../constants/AppConstants');var AppActions = {    showForm: function(){        // console.log('AppAction: Searching for movie' + movie.title);        AppDispatcher.handleViewAction({            actionType: AppConstants.SHOW_FORM        });    },    addWorkout:function(workout){        AppDispatcher.handleViewAction({            actionType: AppConstants.ADD_WORKOUT,            workout:workout        });    },    receiveWorkouts:function(workouts){        AppDispatcher.handleViewAction({            actionType: AppConstants.RECEIVE_WORKOUTS,            workouts:workouts        });    },    removeWorkout:function(workoutId){        AppDispatcher.handleViewAction({            actionType: AppConstants.REMOVE_WORKOUT,            workoutId:workoutId        });    }}module.exports = AppActions;

js/dispatcher/AppDispatcher.js

var Dispatcher = require('flux').Dispatcher;var assign = require('object-assign');var AppDispatcher = assign(new Dispatcher(),{    handleViewAction: function(action){        var payload = {            source:"VIEW_ACTION",            action: action        }        this.dispatch(payload);    }});module.exports = AppDispatcher;

js/stores/AppStore.js

var AppDispatcher = require('../dispatcher/AppDispatcher');var AppConstants = require('../constants/AppConstants');var EventEmitter = require('events').EventEmitter;var assign = require('object-assign');var AppAPI = require('../utils/AppAPI.js')var CHANGE_EVENT = 'change';var _workouts = [];var _showForm = false;var AppStore = assign({},EventEmitter.prototype,{    showForm: function(){        _showForm = true;    },    getShowForm: function(){        return _showForm;    },    addWorkout:function(workout){        _workouts.push(workout);    },    getWorkouts:function(){        return _workouts;    },    receiveWorkouts:function(workouts){        _workouts = workouts;    },    removeWorkout:function(workoutId){        // var index =_workouts.findIndex(x => x.id === workoutId);        function findIndex(_workouts,workoutId){            for(var i=0,len=_workouts.length;i<len;i++){                if(_workouts[i]._id === workoutId){                    return i;                }            }            return -1;        }        var index = findIndex(_workouts,workoutId);        _workouts.splice(index,1);    },    emitChange:function(){        this.emit(CHANGE_EVENT);    },    addChangeListener: function(callback){        this.on('change',callback);    },    removeChangeListener:function(callback){        this.removeListener('change',callback);    }});AppDispatcher.register(function(payload){    var action = payload.action;    switch(action.actionType){        case AppConstants.SHOW_FORM:            AppStore.showForm();            AppStore.emit(CHANGE_EVENT);            break;        case AppConstants.ADD_WORKOUT:            AppStore.addWorkout(action.workout);            AppAPI.addWorkout(action.workout);            AppStore.emit(CHANGE_EVENT);            break;        case AppConstants.RECEIVE_WORKOUTS:            AppStore.receiveWorkouts(action.workouts);            AppStore.emit(CHANGE_EVENT);            break;        case AppConstants.REMOVE_WORKOUT:            AppStore.removeWorkout(action.workoutId);            AppAPI.removeWorkout(action.workoutId);            AppStore.emit(CHANGE_EVENT);            break;    }    return true;})module.exports = AppStore;

js/utils/AppAPI.js

var AppActions = require('../actions/AppActions');module.exports = {    addWorkout: function(workout){        console.log('Saving Workout...');        var workouts = JSON.parse(localStorage.getItem('workouts'));        workouts.push(workout);        console.log('addWorkout:',workouts);        localStorage.setItem('workouts',JSON.stringify(workouts));    },    getWorkouts:function(){        var workouts = JSON.parse(localStorage.getItem('workouts'));        AppActions.receiveWorkouts(workouts);    },    removeWorkout:function(workoutId){        var workouts = JSON.parse(localStorage.getItem('workouts'));        for(var i=0;i<workouts.length;i++){            if(workouts[i].id ==workoutId){                workouts.splice(i,1);            }        }        localStorage.setItem('workouts',JSON.stringify(workouts));    }}

js/startData.js

module.exports ={    init:function(){        localStorage.clear();        localStorage.setItem('workouts',JSON.stringify([        {            id:0001,            type:'Jogging',            minutes:20,            miles:2,            date: new Date()        },        {            id:0002,            type:'Yoga',            minute:40,            miles:'',            date:new Date()        }        ]));    }}
原创粉丝点击