From 8eee90b83fa27e162913496d5e8b13e83b86de65 Mon Sep 17 00:00:00 2001 From: Ken Edwards Date: Fri, 5 May 2017 13:43:27 -0700 Subject: [PATCH 1/4] lessons 1-7 completed --- client/client.js | 7 +++++++ client/index.html | 1 - components/App.js | 17 +++++++++++++++++ components/TextDisplay.js | 18 ++++++++++++++++++ components/TextInput.js | 36 ++++++++++++++++++++++++++++++++++++ package.json | 5 ++++- server/server.js | 8 ++++++++ webpack.config.js | 30 ++++++++++++++++++++++++++++++ 8 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 components/TextDisplay.js create mode 100644 components/TextInput.js diff --git a/client/client.js b/client/client.js index 8b13789..0fc43e3 100644 --- a/client/client.js +++ b/client/client.js @@ -1 +1,8 @@ +import React from 'react'; +import {render} from 'react-dom'; +import App from '../components/App'; +render ( +, document.getElementById('app') + +) diff --git a/client/index.html b/client/index.html index 7a2106d..4c0c135 100644 --- a/client/index.html +++ b/client/index.html @@ -5,7 +5,6 @@ React Todo List -

This is not a React app yet!

diff --git a/components/App.js b/components/App.js index 8b13789..e7cd4f5 100644 --- a/components/App.js +++ b/components/App.js @@ -1 +1,18 @@ +import React, {Component} from 'react'; +import TextInput from './TextInput'; +class App extends Component { + + render(){ + return( +
+ +

This is the App Component Man

+ + +
+ ) + } +} + +export default App; diff --git a/components/TextDisplay.js b/components/TextDisplay.js new file mode 100644 index 0000000..8139a0c --- /dev/null +++ b/components/TextDisplay.js @@ -0,0 +1,18 @@ +import React, { Component } from 'react' + +class TextDisplay extends Component { + + handleClick() { + this.props.deleteLetter() + } + + render() { + return ( +
+
I'm displaying text from my parent: {this.props.text}
+ +
+ ) + } +} +export default TextDisplay diff --git a/components/TextInput.js b/components/TextInput.js new file mode 100644 index 0000000..3b455c8 --- /dev/null +++ b/components/TextInput.js @@ -0,0 +1,36 @@ +import React, {Component} from 'react'; +import TextDisplay from './TextDisplay'; + +class TextInput extends Component { + constructor(props){ + super(props) + this.state = { + inputText: 'initial words' + } + } + + deleteLetter() { + this.setState({ + inputText: this.state.inputText.substring(0, this.state.inputText.length - 1) + }) + } + + handleChange(event){ + this.setState({inputText: event.target.value}) + } + + render(){ + return ( +
+ + +
+ ) + } +} + +export default TextInput; diff --git a/package.json b/package.json index 034be19..f1f712b 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,12 @@ "babel-loader": "^6.2.2", "babel-preset-es2015": "^6.3.13", "babel-preset-react": "^6.3.13", + "babel-preset-react-hmre": "^1.1.1", "express": "^4.13.4", "react": "^0.14.7", "react-dom": "^0.14.7", - "webpack": "^1.12.13" + "webpack": "^1.12.13", + "webpack-dev-middleware": "^1.10.2", + "webpack-hot-middleware": "^2.18.0" } } diff --git a/server/server.js b/server/server.js index 51a4946..c6d2160 100644 --- a/server/server.js +++ b/server/server.js @@ -1,8 +1,16 @@ var express = require('express'); var path = require('path'); +var config = require('../webpack.config.js'); +var webpack = require('webpack'); +var webpackDevMiddleware = require('webpack-dev-middleware'); +var webpackHotMiddleware = require('webpack-hot-middleware'); var app = express(); +var compiler = webpack(config); +app.use(webpackDevMiddleware(compiler, {noInfo: true, publicPath: config.output.publicPath})); +app.use(webpackHotMiddleware(compiler)); + app.use(express.static('./dist')); app.use('/', function (req, res) { diff --git a/webpack.config.js b/webpack.config.js index 8b13789..ae5bfd8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1 +1,31 @@ +const webpack = require('webpack'); +module.exports = { + devtool:'inline-source-map', + entry: [ + 'webpack-hot-middleware/client', + './client/client.js' + ], + output: { + path: require('path').resolve('./dist'), + filename: 'bundle.js', + publicPath: '/', + hot: true + }, + plugins:[ + new webpack.HotModuleReplacementPlugin(), + new webpack.NoErrorsPlugin() + ], + module: { + loaders: [ + { + test: /\.js$/, + loader: 'babel-loader', + exclude: /node_modules/, + query: { + presets: ['react', 'es2015', 'react-hmre'] + } + } + ] + } +} From 3d0fe7f49aef51b83d29fdd8252ddd1ed49918df Mon Sep 17 00:00:00 2001 From: Ken Edwards Date: Fri, 5 May 2017 16:28:48 -0700 Subject: [PATCH 2/4] lesson 8 completed --- client/client.js | 22 +++++++++++++++++++++- components/App.js | 18 ++++++++++++------ components/TextDisplay.js | 18 ------------------ components/TextInput.js | 36 ------------------------------------ components/TodoInput.js | 37 +++++++++++++++++++++++++++++++++++++ components/TodoList.js | 19 +++++++++++++++++++ package.json | 3 +++ redux/actions.js | 10 ++++++++++ redux/reducer.js | 24 ++++++++++++++++++++++++ redux/store.js | 12 ++++++++++++ 10 files changed, 138 insertions(+), 61 deletions(-) delete mode 100644 components/TextDisplay.js delete mode 100644 components/TextInput.js create mode 100644 components/TodoInput.js create mode 100644 components/TodoList.js create mode 100644 redux/actions.js create mode 100644 redux/reducer.js create mode 100644 redux/store.js diff --git a/client/client.js b/client/client.js index 0fc43e3..2464ca2 100644 --- a/client/client.js +++ b/client/client.js @@ -1,8 +1,28 @@ import React from 'react'; import {render} from 'react-dom'; import App from '../components/App'; +import configureStore from '../redux/store'; +import { Provider } from 'react-redux'; + +//configure and create our store +//var store = createStore (reducers, initialState) // [] + +let initialState = { + todos: [{ + id: 0, + completed: false, + text: 'Initial todo for demo purposes' + }] +} + +let store = configureStore(initialState) + + render ( -, document.getElementById('app') + + + +, document.getElementById('app') ) diff --git a/components/App.js b/components/App.js index e7cd4f5..47892b0 100644 --- a/components/App.js +++ b/components/App.js @@ -1,18 +1,24 @@ import React, {Component} from 'react'; -import TextInput from './TextInput'; +import TodoInput from './TodoInput'; +import TodoList from './TodoList'; +import { connect } from 'react-redux'; -class App extends Component { +class App extends Component { render(){ return(
-

This is the App Component Man

- - +

Todo List

+ +
) } } -export default App; +function mapStateToProps(state) { + return state +} + +export default connect(mapStateToProps)(App); diff --git a/components/TextDisplay.js b/components/TextDisplay.js deleted file mode 100644 index 8139a0c..0000000 --- a/components/TextDisplay.js +++ /dev/null @@ -1,18 +0,0 @@ -import React, { Component } from 'react' - -class TextDisplay extends Component { - - handleClick() { - this.props.deleteLetter() - } - - render() { - return ( -
-
I'm displaying text from my parent: {this.props.text}
- -
- ) - } -} -export default TextDisplay diff --git a/components/TextInput.js b/components/TextInput.js deleted file mode 100644 index 3b455c8..0000000 --- a/components/TextInput.js +++ /dev/null @@ -1,36 +0,0 @@ -import React, {Component} from 'react'; -import TextDisplay from './TextDisplay'; - -class TextInput extends Component { - constructor(props){ - super(props) - this.state = { - inputText: 'initial words' - } - } - - deleteLetter() { - this.setState({ - inputText: this.state.inputText.substring(0, this.state.inputText.length - 1) - }) - } - - handleChange(event){ - this.setState({inputText: event.target.value}) - } - - render(){ - return ( -
- - -
- ) - } -} - -export default TextInput; diff --git a/components/TodoInput.js b/components/TodoInput.js new file mode 100644 index 0000000..a143e95 --- /dev/null +++ b/components/TodoInput.js @@ -0,0 +1,37 @@ +import React, {Component} from 'react'; +import actions from '../redux/actions'; + + +class TodoInput extends Component { + constructor(props){ + super(props) + this.state = { + inputText: '' + } + } + + handleChange(event){ + this.setState({inputText: event.target.value}) + } + +handleSubmit(event) { + event.preventDefault() + this.props.dispatch(actions.addTodo(this.state.inputText)) +} + + + render(){ + return ( +
+ + +
+ ) + } +} + +export default TodoInput; diff --git a/components/TodoList.js b/components/TodoList.js new file mode 100644 index 0000000..612606a --- /dev/null +++ b/components/TodoList.js @@ -0,0 +1,19 @@ +import React, {Component} from 'react'; + +class TodoList extends Component { + + render(){ + return ( +
    + + { + this.props.todos.map((todo) => { + return
  • {todo.text}
  • + }) + } + +
+ ) + } +} +export default TodoList; diff --git a/package.json b/package.json index f1f712b..22e9495 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,9 @@ "express": "^4.13.4", "react": "^0.14.7", "react-dom": "^0.14.7", + "react-redux": "^5.0.4", + "redux": "^3.6.0", + "redux-logger": "^3.0.1", "webpack": "^1.12.13", "webpack-dev-middleware": "^1.10.2", "webpack-hot-middleware": "^2.18.0" diff --git a/redux/actions.js b/redux/actions.js new file mode 100644 index 0000000..b6c733c --- /dev/null +++ b/redux/actions.js @@ -0,0 +1,10 @@ +let actions = { + addTodo: function(text) { + return { + type: 'ADD_TODO', + text: text + } + } +} + +export default actions; diff --git a/redux/reducer.js b/redux/reducer.js new file mode 100644 index 0000000..1db23e9 --- /dev/null +++ b/redux/reducer.js @@ -0,0 +1,24 @@ +function getId(state){ + return state.todos.reduce((maxId, todo) => { + return Math.max(todo.id, maxId) + },-1) + 1 +} + + +let reducer = function (state, action) { + switch (action.type) { + case 'ADD_TODO': + console.log('got to correct add todo'); + return Object.assign({}, state, { + todos: [{ + text: action.text, + completed: false, + id: getId(state) + }, ...state.todos] + }) + default: + return state; + } +} + +export default reducer; diff --git a/redux/store.js b/redux/store.js new file mode 100644 index 0000000..9d9e79b --- /dev/null +++ b/redux/store.js @@ -0,0 +1,12 @@ +import { applyMiddleware, compose, createStore } from 'redux'; +import reducer from './reducer'; +import { createLogger } from 'redux-logger'; + +let finalCreateStore = compose ( + applyMiddleware(createLogger()) +)(createStore) + + +export default function configureStore (initialState = { todos: [] }) { + return createStore(reducer, initialState) +} From e494aa4ab0ea8ab33d8c5bb0b947e1fd5e90561e Mon Sep 17 00:00:00 2001 From: Ken Edwards Date: Sat, 6 May 2017 11:00:46 -0700 Subject: [PATCH 3/4] update --- components/TodoInput.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 components/TodoInput.js diff --git a/components/TodoInput.js b/components/TodoInput.js new file mode 100644 index 0000000..87cd41f --- /dev/null +++ b/components/TodoInput.js @@ -0,0 +1,36 @@ +import React, {Component} from 'react'; +import actions from '../redux/actions'; + + +class TodoInput extends Component { + constructor(props){ + super(props) + this.state = { + inputText: '' + } + } + handleChange(event){ + this.setState({inputText: event.target.value}) + } + +handleSubmit(event) { + event.preventDefault() + this.props.dispatch(actions.addTodo(this.state.inputText)) +} + + + render(){ + return ( +
+ + +
+ ) + } +} + +export default TodoInput; From 1ece37f783eba798ea39f74112e133cd31360792 Mon Sep 17 00:00:00 2001 From: Ken Edwards Date: Tue, 9 May 2017 12:24:06 -0700 Subject: [PATCH 4/4] lesson 9 completed --- components/App.js | 15 ++++++++++++--- components/TodoInput.js | 8 ++++---- components/TodoItem.js | 27 +++++++++++++++++++++++++++ components/TodoList.js | 3 ++- redux/actions.js | 14 ++++++++++++++ redux/reducer.js | 15 +++++++++++++++ 6 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 components/TodoItem.js diff --git a/components/App.js b/components/App.js index 47892b0..6f50b56 100644 --- a/components/App.js +++ b/components/App.js @@ -2,6 +2,8 @@ import React, {Component} from 'react'; import TodoInput from './TodoInput'; import TodoList from './TodoList'; import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import actions from '../redux/actions'; class App extends Component { @@ -10,8 +12,8 @@ class App extends Component {

Todo List

- - + +
) } @@ -21,4 +23,11 @@ function mapStateToProps(state) { return state } -export default connect(mapStateToProps)(App); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators(actions, dispatch) + } +} + + +export default connect(mapStateToProps, mapDispatchToProps)(App); diff --git a/components/TodoInput.js b/components/TodoInput.js index a143e95..8bdc86f 100644 --- a/components/TodoInput.js +++ b/components/TodoInput.js @@ -1,6 +1,4 @@ import React, {Component} from 'react'; -import actions from '../redux/actions'; - class TodoInput extends Component { constructor(props){ @@ -16,19 +14,21 @@ class TodoInput extends Component { handleSubmit(event) { event.preventDefault() - this.props.dispatch(actions.addTodo(this.state.inputText)) + this.props.addTodo(this.state.inputText) } render(){ return (
+
- + +
) } diff --git a/components/TodoItem.js b/components/TodoItem.js new file mode 100644 index 0000000..4dc6c02 --- /dev/null +++ b/components/TodoItem.js @@ -0,0 +1,27 @@ +import React, {Component} from 'react'; + +class TodoItem extends Component { + + handleComplete () { + this.props.actions.completeTodo(this.props.todo.id) + } + + handleDelete(){ + this.props.actions.deleteTodo(this.props.todo.id) + + } + + + + render(){ + return ( +
  • +
    {this.props.todo.text}
    + + + +
  • + ) + } +} +export default TodoItem; diff --git a/components/TodoList.js b/components/TodoList.js index 612606a..f184f06 100644 --- a/components/TodoList.js +++ b/components/TodoList.js @@ -1,4 +1,5 @@ import React, {Component} from 'react'; +import TodoItem from './TodoItem'; class TodoList extends Component { @@ -8,7 +9,7 @@ class TodoList extends Component { { this.props.todos.map((todo) => { - return
  • {todo.text}
  • + return }) } diff --git a/redux/actions.js b/redux/actions.js index b6c733c..d8baf24 100644 --- a/redux/actions.js +++ b/redux/actions.js @@ -4,6 +4,20 @@ let actions = { type: 'ADD_TODO', text: text } + }, + + completeTodo: function (id){ + return { + type: 'COMPLETE_TODO', + id: id + } + }, + + deleteTodo: function (id) { + return { + type: 'DELETE_TODO', + id:id + } } } diff --git a/redux/reducer.js b/redux/reducer.js index 1db23e9..d2ce8fd 100644 --- a/redux/reducer.js +++ b/redux/reducer.js @@ -16,6 +16,21 @@ let reducer = function (state, action) { id: getId(state) }, ...state.todos] }) + case 'COMPLETE_TODO': + console.log('COMPLETE_TODO'); + return Object.assign({}, state, { + todos: state.todos.map((todo) => { + return todo.id === action.id ? + Object.assign({}, todo, {completed: !todo.completed}) : todo + }) + }) + case 'DELETE_TODO': + console.log('DELETE_TODO'); + return Object.assign({}, state, { + todos: state.todos.filter((todo) => { + return todo.id !== action.id + }) + }) default: return state; }