├── level-21_redux-store ├── reducers │ ├── index.js │ └── todos.js ├── dispatcher │ └── AppDispatcher.js ├── constants │ └── ActionTypes.js ├── todos.json ├── main.js ├── components │ ├── CreateTodoFieldContainer.js │ ├── TodoApp.js │ ├── TodoHeader.js │ ├── TodoHeaderContainer.js │ ├── TodoListContainer.js │ ├── TodoList.js │ ├── InputField.js │ └── TodoItem.js ├── actions │ └── TodoActions.js ├── stores │ └── TodoStore.js └── index.html ├── level-24_react-redux ├── reducers │ ├── index.js │ └── todos.js ├── constants │ └── ActionTypes.js ├── todos.json ├── components │ ├── TodoHeaderContainer.js │ ├── CreateTodoFieldContainer.js │ ├── TodoApp.js │ ├── TodoHeader.js │ ├── TodoListContainer.js │ ├── TodoList.js │ ├── InputField.js │ └── TodoItem.js ├── main.js ├── actions │ └── TodoActions.js └── index.html ├── level-25_immutablejs ├── reducers │ ├── index.js │ └── todos.js ├── constants │ └── ActionTypes.js ├── todos.json ├── components │ ├── TodoHeaderContainer.js │ ├── CreateTodoFieldContainer.js │ ├── TodoApp.js │ ├── TodoHeader.js │ ├── TodoListContainer.js │ ├── TodoList.js │ ├── InputField.js │ └── TodoItem.js ├── main.js ├── actions │ └── TodoActions.js └── index.html ├── level-20_redux-reducers ├── reducers │ ├── index.js │ └── todos.js ├── dispatcher │ └── AppDispatcher.js ├── constants │ └── ActionTypes.js ├── todos.json ├── components │ ├── CreateTodoFieldContainer.js │ ├── TodoApp.js │ ├── TodoHeader.js │ ├── TodoHeaderContainer.js │ ├── TodoListContainer.js │ ├── TodoList.js │ ├── InputField.js │ └── TodoItem.js ├── actions │ └── TodoActions.js ├── stores │ └── TodoStore.js └── index.html ├── level-22_redux-actions ├── reducers │ ├── index.js │ └── todos.js ├── dispatcher │ └── AppDispatcher.js ├── constants │ └── ActionTypes.js ├── todos.json ├── main.js ├── components │ ├── CreateTodoFieldContainer.js │ ├── TodoApp.js │ ├── TodoHeader.js │ ├── TodoHeaderContainer.js │ ├── TodoListContainer.js │ ├── TodoList.js │ ├── InputField.js │ └── TodoItem.js ├── actions │ └── TodoActions.js ├── stores │ └── TodoStore.js └── index.html ├── level-23_redux-middlewares ├── reducers │ ├── index.js │ └── todos.js ├── dispatcher │ └── AppDispatcher.js ├── constants │ └── ActionTypes.js ├── todos.json ├── components │ ├── CreateTodoFieldContainer.js │ ├── TodoApp.js │ ├── TodoHeader.js │ ├── TodoHeaderContainer.js │ ├── TodoListContainer.js │ ├── TodoList.js │ ├── InputField.js │ └── TodoItem.js ├── main.js ├── actions │ └── TodoActions.js ├── stores │ └── TodoStore.js └── index.html ├── level-14_flux-actions ├── dispatcher │ └── AppDispatcher.js ├── constants │ └── ActionTypes.js ├── todos.json ├── components │ ├── TodoHeader.js │ ├── TodoList.js │ ├── InputField.js │ ├── TodoItem.js │ └── TodoApp.js ├── actions │ └── TodoActions.js └── index.html ├── level-15_flux-stores ├── dispatcher │ └── AppDispatcher.js ├── constants │ └── ActionTypes.js ├── todos.json ├── components │ ├── TodoHeader.js │ ├── TodoList.js │ ├── InputField.js │ ├── TodoItem.js │ └── TodoApp.js ├── actions │ └── TodoActions.js ├── index.html └── stores │ └── TodoStore.js ├── level-18_flux-utils ├── dispatcher │ └── AppDispatcher.js ├── constants │ └── ActionTypes.js ├── todos.json ├── components │ ├── CreateTodoFieldContainer.js │ ├── TodoApp.js │ ├── TodoHeader.js │ ├── TodoHeaderContainer.js │ ├── TodoListContainer.js │ ├── TodoList.js │ ├── InputField.js │ └── TodoItem.js ├── actions │ └── TodoActions.js ├── index.html └── stores │ └── TodoStore.js ├── level-13_flux-dispatcher ├── dispatcher │ └── AppDispatcher.js ├── todos.json ├── components │ ├── TodoHeader.js │ ├── TodoList.js │ ├── InputField.js │ ├── TodoItem.js │ └── TodoApp.js └── index.html ├── level-17_container-pattern ├── dispatcher │ └── AppDispatcher.js ├── constants │ └── ActionTypes.js ├── todos.json ├── components │ ├── CreateTodoFieldContainer.js │ ├── TodoApp.js │ ├── TodoHeader.js │ ├── TodoHeaderContainer.js │ ├── TodoListContainer.js │ ├── TodoList.js │ ├── InputField.js │ └── TodoItem.js ├── actions │ └── TodoActions.js ├── index.html └── stores │ └── TodoStore.js ├── level-16_flux-controller-view ├── dispatcher │ └── AppDispatcher.js ├── constants │ └── ActionTypes.js ├── todos.json ├── components │ ├── TodoHeader.js │ ├── TodoList.js │ ├── InputField.js │ ├── TodoApp.js │ └── TodoItem.js ├── actions │ └── TodoActions.js ├── index.html └── stores │ └── TodoStore.js ├── assets ├── flux-diagram.png ├── level-03_demo.png ├── level-04_demo.png ├── level-06_demo.png ├── level-09_demo.gif ├── redux-diagram.png ├── level-05_demo-1.png ├── level-05_demo-2.png ├── level-07_demo-1.png ├── level-07_demo-2.png ├── todoapp-markup.png ├── todoapp-components.png └── component-lifecycle.jpg ├── level-04_first-component ├── TodoApp.js └── index.html ├── level-05_component-composition ├── InputField.js ├── TodoHeader.js ├── TodoItem.js ├── TodoApp.js ├── TodoList.js └── index.html ├── level-08_dynamic-children ├── InputField.js ├── TodoItem.js ├── TodoList.js ├── TodoHeader.js ├── TodoApp.js └── index.html ├── level-06_transferring-props ├── InputField.js ├── TodoHeader.js ├── TodoItem.js ├── TodoApp.js ├── TodoList.js └── index.html ├── level-09_stateful-component ├── InputField.js ├── TodoHeader.js ├── TodoList.js ├── index.html ├── TodoApp.js └── TodoItem.js ├── level-07_prop-types-n-default-values ├── InputField.js ├── TodoApp.js ├── TodoItem.js ├── TodoHeader.js ├── TodoList.js └── index.html ├── level-11_component-lifecycle ├── todos.json ├── TodoHeader.js ├── TodoList.js ├── index.html ├── InputField.js ├── TodoItem.js └── TodoApp.js ├── level-02_initial-project └── index.html ├── level-03_hello-react └── index.html ├── level-10_forms ├── TodoHeader.js ├── index.html ├── TodoList.js ├── InputField.js └── TodoItem.js ├── level-01_react └── README.md ├── level-12_flux └── README.md └── level-19_redux └── README.md /level-21_redux-store/reducers/index.js: -------------------------------------------------------------------------------- 1 | window.App.reducers = {}; 2 | -------------------------------------------------------------------------------- /level-24_react-redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | window.App.reducers = {}; 2 | -------------------------------------------------------------------------------- /level-25_immutablejs/reducers/index.js: -------------------------------------------------------------------------------- 1 | window.App.reducers = {}; 2 | -------------------------------------------------------------------------------- /level-20_redux-reducers/reducers/index.js: -------------------------------------------------------------------------------- 1 | window.App.reducers = {}; 2 | -------------------------------------------------------------------------------- /level-22_redux-actions/reducers/index.js: -------------------------------------------------------------------------------- 1 | window.App.reducers = {}; 2 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/reducers/index.js: -------------------------------------------------------------------------------- 1 | window.App.reducers = {}; 2 | -------------------------------------------------------------------------------- /level-14_flux-actions/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /level-15_flux-stores/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /level-18_flux-utils/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /level-21_redux-store/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /level-13_flux-dispatcher/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /level-17_container-pattern/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /level-20_redux-reducers/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /level-22_redux-actions/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | window.App.AppDispatcher = new Flux.Dispatcher(); 2 | -------------------------------------------------------------------------------- /assets/flux-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/flux-diagram.png -------------------------------------------------------------------------------- /assets/level-03_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/level-03_demo.png -------------------------------------------------------------------------------- /assets/level-04_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/level-04_demo.png -------------------------------------------------------------------------------- /assets/level-06_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/level-06_demo.png -------------------------------------------------------------------------------- /assets/level-09_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/level-09_demo.gif -------------------------------------------------------------------------------- /assets/redux-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/redux-diagram.png -------------------------------------------------------------------------------- /assets/level-05_demo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/level-05_demo-1.png -------------------------------------------------------------------------------- /assets/level-05_demo-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/level-05_demo-2.png -------------------------------------------------------------------------------- /assets/level-07_demo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/level-07_demo-1.png -------------------------------------------------------------------------------- /assets/level-07_demo-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/level-07_demo-2.png -------------------------------------------------------------------------------- /assets/todoapp-markup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/todoapp-markup.png -------------------------------------------------------------------------------- /assets/todoapp-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/todoapp-components.png -------------------------------------------------------------------------------- /assets/component-lifecycle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiningjason/react-quick-tutorial/HEAD/assets/component-lifecycle.jpg -------------------------------------------------------------------------------- /level-04_first-component/TodoApp.js: -------------------------------------------------------------------------------- 1 | class TodoApp extends React.Component { 2 | render() { 3 | return
TodoApp
; 4 | } 5 | } 6 | 7 | window.App.TodoApp = TodoApp; 8 | -------------------------------------------------------------------------------- /level-05_component-composition/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | render() { 3 | return ; 4 | } 5 | } 6 | 7 | window.App.InputField = InputField; 8 | -------------------------------------------------------------------------------- /level-08_dynamic-children/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | render() { 3 | return ( 4 | 5 | ); 6 | } 7 | } 8 | 9 | window.App.InputField = InputField; 10 | -------------------------------------------------------------------------------- /level-06_transferring-props/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | render() { 3 | return ( 4 | 5 | ); 6 | } 7 | } 8 | 9 | window.App.InputField = InputField; 10 | -------------------------------------------------------------------------------- /level-09_stateful-component/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | render() { 3 | return ( 4 | 5 | ); 6 | } 7 | } 8 | 9 | window.App.InputField = InputField; 10 | -------------------------------------------------------------------------------- /level-07_prop-types-n-default-values/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | render() { 3 | return ( 4 | 5 | ); 6 | } 7 | } 8 | 9 | window.App.InputField = InputField; 10 | -------------------------------------------------------------------------------- /level-14_flux-actions/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-15_flux-stores/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-18_flux-utils/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-21_redux-store/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-22_redux-actions/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-24_react-redux/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-25_immutablejs/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-17_container-pattern/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-20_redux-reducers/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | window.App.ActionTypes = { 2 | LOAD_TODOS_SUCCESS: 'LOAD_TODOS_SUCCESS', 3 | CREATE_TODO: 'CREATE_TODO', 4 | UPDATE_TODO: 'UPDATE_TODO', 5 | TOGGLE_TODO: 'TOGGLE_TODO', 6 | DELETE_TODO: 'DELETE_TODO' 7 | }; 8 | -------------------------------------------------------------------------------- /level-05_component-composition/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | return ( 4 |
5 |

我的待辦清單

6 | 哈囉,Jason:你有 99 項未完成待辦事項 7 |
8 | ); 9 | } 10 | } 11 | 12 | window.App.TodoHeader = TodoHeader; 13 | -------------------------------------------------------------------------------- /level-18_flux-utils/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-13_flux-dispatcher/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-14_flux-actions/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-15_flux-stores/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-20_redux-reducers/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-21_redux-store/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-22_redux-actions/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-24_react-redux/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-25_immutablejs/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-11_component-lifecycle/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-17_container-pattern/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "Item 1", 5 | "completed": false 6 | }, 7 | { 8 | "id": 1, 9 | "title": "Item 2", 10 | "completed": false 11 | }, 12 | { 13 | "id": 2, 14 | "title": "Item 3", 15 | "completed": false 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /level-05_component-composition/TodoItem.js: -------------------------------------------------------------------------------- 1 | class TodoItem extends React.Component { 2 | render() { 3 | return ( 4 |
5 | 6 | Item 1 7 | 8 |
9 | ); 10 | } 11 | } 12 | 13 | window.App.TodoItem = TodoItem; 14 | -------------------------------------------------------------------------------- /level-21_redux-store/main.js: -------------------------------------------------------------------------------- 1 | const { createStore, combineReducers } = Redux; 2 | const { TodoApp, reducers } = window.App; 3 | 4 | const composedReducer = combineReducers(reducers); 5 | const store = createStore(composedReducer); 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('app') 10 | ); 11 | -------------------------------------------------------------------------------- /level-22_redux-actions/main.js: -------------------------------------------------------------------------------- 1 | const { createStore, combineReducers } = Redux; 2 | const { TodoApp, reducers } = window.App; 3 | 4 | const composedReducer = combineReducers(reducers); 5 | const store = createStore(composedReducer); 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('app') 10 | ); 11 | -------------------------------------------------------------------------------- /level-05_component-composition/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | InputField, 3 | TodoHeader, 4 | TodoList 5 | } = window.App; 6 | 7 | class TodoApp extends React.Component { 8 | render() { 9 | return ( 10 |
11 | 12 | 13 | 14 |
15 | ); 16 | } 17 | } 18 | 19 | window.App.TodoApp = TodoApp; 20 | -------------------------------------------------------------------------------- /level-06_transferring-props/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
10 |

{title}

11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
13 | ); 14 | } 15 | } 16 | 17 | window.App.TodoHeader = TodoHeader; 18 | -------------------------------------------------------------------------------- /level-06_transferring-props/TodoItem.js: -------------------------------------------------------------------------------- 1 | class TodoItem extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | completed 6 | } = this.props; 7 | return ( 8 |
9 | 10 | {title} 11 | 12 |
13 | ); 14 | } 15 | } 16 | 17 | window.App.TodoItem = TodoItem; 18 | -------------------------------------------------------------------------------- /level-18_flux-utils/components/CreateTodoFieldContainer.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | InputField 4 | } = window.App; 5 | 6 | class CreateTodoFieldContainer extends React.Component { 7 | render() { 8 | return ( 9 | 13 | ); 14 | } 15 | } 16 | 17 | window.App.CreateTodoFieldContainer = CreateTodoFieldContainer; 18 | -------------------------------------------------------------------------------- /level-05_component-composition/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | return ( 6 |
    7 |
  • 8 | 9 |
  • 10 |
  • 11 | 12 |
  • 13 |
  • 14 | 15 |
  • 16 |
17 | ); 18 | } 19 | } 20 | 21 | window.App.TodoList = TodoList; 22 | -------------------------------------------------------------------------------- /level-20_redux-reducers/components/CreateTodoFieldContainer.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | InputField 4 | } = window.App; 5 | 6 | class CreateTodoFieldContainer extends React.Component { 7 | render() { 8 | return ( 9 | 13 | ); 14 | } 15 | } 16 | 17 | window.App.CreateTodoFieldContainer = CreateTodoFieldContainer; 18 | -------------------------------------------------------------------------------- /level-21_redux-store/components/CreateTodoFieldContainer.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | InputField 4 | } = window.App; 5 | 6 | class CreateTodoFieldContainer extends React.Component { 7 | render() { 8 | return ( 9 | 13 | ); 14 | } 15 | } 16 | 17 | window.App.CreateTodoFieldContainer = CreateTodoFieldContainer; 18 | -------------------------------------------------------------------------------- /level-22_redux-actions/components/CreateTodoFieldContainer.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | InputField 4 | } = window.App; 5 | 6 | class CreateTodoFieldContainer extends React.Component { 7 | render() { 8 | return ( 9 | 13 | ); 14 | } 15 | } 16 | 17 | window.App.CreateTodoFieldContainer = CreateTodoFieldContainer; 18 | -------------------------------------------------------------------------------- /level-17_container-pattern/components/CreateTodoFieldContainer.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | InputField 4 | } = window.App; 5 | 6 | class CreateTodoFieldContainer extends React.Component { 7 | render() { 8 | return ( 9 | 13 | ); 14 | } 15 | } 16 | 17 | window.App.CreateTodoFieldContainer = CreateTodoFieldContainer; 18 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/components/CreateTodoFieldContainer.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | InputField 4 | } = window.App; 5 | 6 | class CreateTodoFieldContainer extends React.Component { 7 | render() { 8 | return ( 9 | 13 | ); 14 | } 15 | } 16 | 17 | window.App.CreateTodoFieldContainer = CreateTodoFieldContainer; 18 | -------------------------------------------------------------------------------- /level-02_initial-project/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
hello, world
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /level-06_transferring-props/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | InputField, 3 | TodoHeader, 4 | TodoList 5 | } = window.App; 6 | 7 | class TodoApp extends React.Component { 8 | render() { 9 | return ( 10 |
11 | 16 | 17 | 18 |
19 | ); 20 | } 21 | } 22 | 23 | window.App.TodoApp = TodoApp; 24 | -------------------------------------------------------------------------------- /level-07_prop-types-n-default-values/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | InputField, 3 | TodoHeader, 4 | TodoList 5 | } = window.App; 6 | 7 | class TodoApp extends React.Component { 8 | render() { 9 | return ( 10 |
11 | 16 | 17 | 18 |
19 | ); 20 | } 21 | } 22 | 23 | window.App.TodoApp = TodoApp; 24 | -------------------------------------------------------------------------------- /level-18_flux-utils/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | CreateTodoFieldContainer, 4 | TodoHeaderContainer, 5 | TodoListContainer 6 | } = window.App; 7 | 8 | class TodoApp extends React.Component { 9 | componentDidMount() { 10 | TodoActions.loadTodos(); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | window.App.TodoApp = TodoApp; 25 | -------------------------------------------------------------------------------- /level-21_redux-store/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | CreateTodoFieldContainer, 4 | TodoHeaderContainer, 5 | TodoListContainer 6 | } = window.App; 7 | 8 | class TodoApp extends React.Component { 9 | componentDidMount() { 10 | TodoActions.loadTodos(); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | window.App.TodoApp = TodoApp; 25 | -------------------------------------------------------------------------------- /level-22_redux-actions/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | CreateTodoFieldContainer, 4 | TodoHeaderContainer, 5 | TodoListContainer 6 | } = window.App; 7 | 8 | class TodoApp extends React.Component { 9 | componentDidMount() { 10 | TodoActions.loadTodos(); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | window.App.TodoApp = TodoApp; 25 | -------------------------------------------------------------------------------- /level-24_react-redux/components/TodoHeaderContainer.js: -------------------------------------------------------------------------------- 1 | const { connect } = ReactRedux; 2 | 3 | const { TodoHeader } = window.App; 4 | 5 | class TodoHeaderContainer extends React.Component { 6 | render() { 7 | return ( 8 | !todo.completed).length} 12 | /> 13 | ); 14 | } 15 | } 16 | 17 | window.App.TodoHeaderContainer = connect( 18 | (state) => ({ todos: state.todos }) 19 | )(TodoHeaderContainer); 20 | -------------------------------------------------------------------------------- /level-25_immutablejs/components/TodoHeaderContainer.js: -------------------------------------------------------------------------------- 1 | const { connect } = ReactRedux; 2 | 3 | const { TodoHeader } = window.App; 4 | 5 | class TodoHeaderContainer extends React.Component { 6 | render() { 7 | return ( 8 | !todo.completed).size} 12 | /> 13 | ); 14 | } 15 | } 16 | 17 | window.App.TodoHeaderContainer = connect( 18 | (state) => ({ todos: state.todos }) 19 | )(TodoHeaderContainer); 20 | -------------------------------------------------------------------------------- /level-08_dynamic-children/TodoItem.js: -------------------------------------------------------------------------------- 1 | class TodoItem extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | completed 6 | } = this.props; 7 | return ( 8 |
9 | 10 | {title} 11 | 12 |
13 | ); 14 | } 15 | } 16 | 17 | TodoItem.propTypes = { 18 | title: React.PropTypes.string.isRequired, 19 | completed: React.PropTypes.bool.isRequired 20 | }; 21 | 22 | window.App.TodoItem = TodoItem; 23 | -------------------------------------------------------------------------------- /level-17_container-pattern/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | CreateTodoFieldContainer, 4 | TodoHeaderContainer, 5 | TodoListContainer 6 | } = window.App; 7 | 8 | class TodoApp extends React.Component { 9 | componentDidMount() { 10 | TodoActions.loadTodos(); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | window.App.TodoApp = TodoApp; 25 | -------------------------------------------------------------------------------- /level-20_redux-reducers/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | CreateTodoFieldContainer, 4 | TodoHeaderContainer, 5 | TodoListContainer 6 | } = window.App; 7 | 8 | class TodoApp extends React.Component { 9 | componentDidMount() { 10 | TodoActions.loadTodos(); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | window.App.TodoApp = TodoApp; 25 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | CreateTodoFieldContainer, 4 | TodoHeaderContainer, 5 | TodoListContainer 6 | } = window.App; 7 | 8 | class TodoApp extends React.Component { 9 | componentDidMount() { 10 | TodoActions.loadTodos(); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | window.App.TodoApp = TodoApp; 25 | -------------------------------------------------------------------------------- /level-24_react-redux/components/CreateTodoFieldContainer.js: -------------------------------------------------------------------------------- 1 | const { connect } = ReactRedux; 2 | 3 | const { 4 | TodoActions, 5 | InputField 6 | } = window.App; 7 | 8 | class CreateTodoFieldContainer extends React.Component { 9 | render() { 10 | return ( 11 | 15 | ); 16 | } 17 | } 18 | 19 | window.App.CreateTodoFieldContainer = connect(undefined, { 20 | createTodo: TodoActions.createTodo 21 | })(CreateTodoFieldContainer); 22 | -------------------------------------------------------------------------------- /level-25_immutablejs/components/CreateTodoFieldContainer.js: -------------------------------------------------------------------------------- 1 | const { connect } = ReactRedux; 2 | 3 | const { 4 | TodoActions, 5 | InputField 6 | } = window.App; 7 | 8 | class CreateTodoFieldContainer extends React.Component { 9 | render() { 10 | return ( 11 | 15 | ); 16 | } 17 | } 18 | 19 | window.App.CreateTodoFieldContainer = connect(undefined, { 20 | createTodo: TodoActions.createTodo 21 | })(CreateTodoFieldContainer); 22 | -------------------------------------------------------------------------------- /level-07_prop-types-n-default-values/TodoItem.js: -------------------------------------------------------------------------------- 1 | class TodoItem extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | completed 6 | } = this.props; 7 | return ( 8 |
9 | 10 | {title} 11 | 12 |
13 | ); 14 | } 15 | } 16 | 17 | TodoItem.propTypes = { 18 | title: React.PropTypes.string.isRequired, 19 | completed: React.PropTypes.bool.isRequired 20 | }; 21 | 22 | window.App.TodoItem = TodoItem; 23 | -------------------------------------------------------------------------------- /level-08_dynamic-children/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { todos } = this.props; 6 | const todoElements = todos.map((todo) => ( 7 |
  • 8 | 12 |
  • 13 | )); 14 | return
      {todoElements}
    ; 15 | } 16 | } 17 | 18 | TodoList.propTypes = { 19 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired 20 | }; 21 | 22 | window.App.TodoList = TodoList; 23 | -------------------------------------------------------------------------------- /level-03_hello-react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/main.js: -------------------------------------------------------------------------------- 1 | const { createStore, combineReducers, applyMiddleware } = Redux; 2 | const { TodoApp, reducers } = window.App; 3 | 4 | const thunkMiddleware = ({ dispatch, getState }) => { 5 | return (next) => (action) => { 6 | if (typeof action === 'function') { 7 | return action(dispatch, getState); 8 | } 9 | return next(action); 10 | }; 11 | }; 12 | 13 | const composedReducer = combineReducers(reducers); 14 | const store = createStore( 15 | composedReducer, 16 | applyMiddleware(thunkMiddleware) 17 | ); 18 | 19 | ReactDOM.render( 20 | , 21 | document.getElementById('app') 22 | ); 23 | -------------------------------------------------------------------------------- /level-24_react-redux/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { connect } = ReactRedux; 2 | const { 3 | TodoActions, 4 | CreateTodoFieldContainer, 5 | TodoHeaderContainer, 6 | TodoListContainer 7 | } = window.App; 8 | 9 | class TodoApp extends React.Component { 10 | componentDidMount() { 11 | this.props.loadTodos(); 12 | } 13 | 14 | render() { 15 | return ( 16 |
    17 | 18 | 19 | 20 |
    21 | ); 22 | } 23 | } 24 | 25 | window.App.TodoApp = connect(undefined, { 26 | loadTodos: TodoActions.loadTodos 27 | })(TodoApp); 28 | -------------------------------------------------------------------------------- /level-25_immutablejs/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { connect } = ReactRedux; 2 | const { 3 | TodoActions, 4 | CreateTodoFieldContainer, 5 | TodoHeaderContainer, 6 | TodoListContainer 7 | } = window.App; 8 | 9 | class TodoApp extends React.Component { 10 | componentDidMount() { 11 | this.props.loadTodos(); 12 | } 13 | 14 | render() { 15 | return ( 16 |
    17 | 18 | 19 | 20 |
    21 | ); 22 | } 23 | } 24 | 25 | window.App.TodoApp = connect(undefined, { 26 | loadTodos: TodoActions.loadTodos 27 | })(TodoApp); 28 | -------------------------------------------------------------------------------- /level-10_forms/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-06_transferring-props/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | return ( 6 |
      7 |
    • 8 | 12 |
    • 13 |
    • 14 | 18 |
    • 19 |
    • 20 | 24 |
    • 25 |
    26 | ); 27 | } 28 | } 29 | 30 | window.App.TodoList = TodoList; 31 | -------------------------------------------------------------------------------- /level-08_dynamic-children/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-09_stateful-component/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-11_component-lifecycle/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-14_flux-actions/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-15_flux-stores/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-18_flux-utils/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-21_redux-store/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-24_react-redux/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-25_immutablejs/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-07_prop-types-n-default-values/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-07_prop-types-n-default-values/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | return ( 6 |
      7 |
    • 8 | 12 |
    • 13 |
    • 14 | 18 |
    • 19 |
    • 20 | 24 |
    • 25 |
    26 | ); 27 | } 28 | } 29 | 30 | window.App.TodoList = TodoList; 31 | -------------------------------------------------------------------------------- /level-13_flux-dispatcher/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-17_container-pattern/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-20_redux-reducers/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-22_redux-actions/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/components/TodoHeader.js: -------------------------------------------------------------------------------- 1 | class TodoHeader extends React.Component { 2 | render() { 3 | const { 4 | title, 5 | username, 6 | todoCount 7 | } = this.props; 8 | return ( 9 |
    10 |

    {title}

    11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項 12 |
    13 | ); 14 | } 15 | } 16 | 17 | TodoHeader.propTypes = { 18 | title: React.PropTypes.string, 19 | username: React.PropTypes.string, 20 | todoCount: React.PropTypes.number 21 | }; 22 | 23 | TodoHeader.defaultProps = { 24 | title: '我的待辦清單', 25 | username: 'Guest', 26 | todoCount: 0 27 | }; 28 | 29 | window.App.TodoHeader = TodoHeader; 30 | -------------------------------------------------------------------------------- /level-24_react-redux/main.js: -------------------------------------------------------------------------------- 1 | const { createStore, combineReducers, applyMiddleware } = Redux; 2 | const { Provider } = ReactRedux; 3 | const { TodoApp, reducers } = window.App; 4 | 5 | const thunkMiddleware = ({ dispatch, getState }) => { 6 | return (next) => (action) => { 7 | if (typeof action === 'function') { 8 | return action(dispatch, getState); 9 | } 10 | return next(action); 11 | }; 12 | }; 13 | 14 | const composedReducer = combineReducers(reducers); 15 | const store = createStore( 16 | composedReducer, 17 | applyMiddleware(thunkMiddleware) 18 | ); 19 | 20 | ReactDOM.render( 21 | 22 | 23 | , 24 | document.getElementById('app') 25 | ); 26 | -------------------------------------------------------------------------------- /level-25_immutablejs/main.js: -------------------------------------------------------------------------------- 1 | const { createStore, combineReducers, applyMiddleware } = Redux; 2 | const { Provider } = ReactRedux; 3 | const { TodoApp, reducers } = window.App; 4 | 5 | const thunkMiddleware = ({ dispatch, getState }) => { 6 | return (next) => (action) => { 7 | if (typeof action === 'function') { 8 | return action(dispatch, getState); 9 | } 10 | return next(action); 11 | }; 12 | }; 13 | 14 | const composedReducer = combineReducers(reducers); 15 | const store = createStore( 16 | composedReducer, 17 | applyMiddleware(thunkMiddleware) 18 | ); 19 | 20 | ReactDOM.render( 21 | 22 | 23 | , 24 | document.getElementById('app') 25 | ); 26 | -------------------------------------------------------------------------------- /level-18_flux-utils/components/TodoHeaderContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoStore, 5 | TodoHeader 6 | } = window.App; 7 | 8 | class TodoHeaderContainer extends React.Component { 9 | static getStores() { 10 | return [ TodoStore ]; 11 | } 12 | 13 | static calculateState(prevState) { 14 | return { 15 | todos: TodoStore.getState(), 16 | }; 17 | } 18 | 19 | render() { 20 | return ( 21 | !todo.completed).length} 25 | /> 26 | ); 27 | } 28 | } 29 | 30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer); 31 | -------------------------------------------------------------------------------- /level-21_redux-store/components/TodoHeaderContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoStore, 5 | TodoHeader 6 | } = window.App; 7 | 8 | class TodoHeaderContainer extends React.Component { 9 | static getStores() { 10 | return [ TodoStore ]; 11 | } 12 | 13 | static calculateState(prevState) { 14 | return { 15 | todos: TodoStore.getState(), 16 | }; 17 | } 18 | 19 | render() { 20 | return ( 21 | !todo.completed).length} 25 | /> 26 | ); 27 | } 28 | } 29 | 30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer); 31 | -------------------------------------------------------------------------------- /level-20_redux-reducers/components/TodoHeaderContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoStore, 5 | TodoHeader 6 | } = window.App; 7 | 8 | class TodoHeaderContainer extends React.Component { 9 | static getStores() { 10 | return [ TodoStore ]; 11 | } 12 | 13 | static calculateState(prevState) { 14 | return { 15 | todos: TodoStore.getState(), 16 | }; 17 | } 18 | 19 | render() { 20 | return ( 21 | !todo.completed).length} 25 | /> 26 | ); 27 | } 28 | } 29 | 30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer); 31 | -------------------------------------------------------------------------------- /level-22_redux-actions/components/TodoHeaderContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoStore, 5 | TodoHeader 6 | } = window.App; 7 | 8 | class TodoHeaderContainer extends React.Component { 9 | static getStores() { 10 | return [ TodoStore ]; 11 | } 12 | 13 | static calculateState(prevState) { 14 | return { 15 | todos: TodoStore.getState(), 16 | }; 17 | } 18 | 19 | render() { 20 | return ( 21 | !todo.completed).length} 25 | /> 26 | ); 27 | } 28 | } 29 | 30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer); 31 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/components/TodoHeaderContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoStore, 5 | TodoHeader 6 | } = window.App; 7 | 8 | class TodoHeaderContainer extends React.Component { 9 | static getStores() { 10 | return [ TodoStore ]; 11 | } 12 | 13 | static calculateState(prevState) { 14 | return { 15 | todos: TodoStore.getState(), 16 | }; 17 | } 18 | 19 | render() { 20 | return ( 21 | !todo.completed).length} 25 | /> 26 | ); 27 | } 28 | } 29 | 30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer); 31 | -------------------------------------------------------------------------------- /level-09_stateful-component/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onDeleteTodo 8 | } = this.props; 9 | const todoElements = todos.map((todo) => ( 10 |
  • 11 | onDeleteTodo && onDeleteTodo(todo.id)} 15 | /> 16 |
  • 17 | )); 18 | return
      {todoElements}
    ; 19 | } 20 | } 21 | 22 | TodoList.propTypes = { 23 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 24 | onDeleteTodo: React.PropTypes.func 25 | }; 26 | 27 | window.App.TodoList = TodoList; 28 | -------------------------------------------------------------------------------- /level-04_first-component/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | -------------------------------------------------------------------------------- /level-18_flux-utils/components/TodoListContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoActions, 5 | TodoStore, 6 | TodoList 7 | } = window.App; 8 | 9 | class TodoListContainer extends React.Component { 10 | static getStores() { 11 | return [ TodoStore ]; 12 | } 13 | 14 | static calculateState(prevState) { 15 | return { 16 | todos: TodoStore.getState(), 17 | }; 18 | } 19 | 20 | render() { 21 | return ( 22 | 28 | ); 29 | } 30 | } 31 | 32 | window.App.TodoListContainer = Container.create(TodoListContainer); 33 | -------------------------------------------------------------------------------- /level-21_redux-store/components/TodoListContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoActions, 5 | TodoStore, 6 | TodoList 7 | } = window.App; 8 | 9 | class TodoListContainer extends React.Component { 10 | static getStores() { 11 | return [ TodoStore ]; 12 | } 13 | 14 | static calculateState(prevState) { 15 | return { 16 | todos: TodoStore.getState(), 17 | }; 18 | } 19 | 20 | render() { 21 | return ( 22 | 28 | ); 29 | } 30 | } 31 | 32 | window.App.TodoListContainer = Container.create(TodoListContainer); 33 | -------------------------------------------------------------------------------- /level-22_redux-actions/components/TodoListContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoActions, 5 | TodoStore, 6 | TodoList 7 | } = window.App; 8 | 9 | class TodoListContainer extends React.Component { 10 | static getStores() { 11 | return [ TodoStore ]; 12 | } 13 | 14 | static calculateState(prevState) { 15 | return { 16 | todos: TodoStore.getState(), 17 | }; 18 | } 19 | 20 | render() { 21 | return ( 22 | 28 | ); 29 | } 30 | } 31 | 32 | window.App.TodoListContainer = Container.create(TodoListContainer); 33 | -------------------------------------------------------------------------------- /level-20_redux-reducers/components/TodoListContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoActions, 5 | TodoStore, 6 | TodoList 7 | } = window.App; 8 | 9 | class TodoListContainer extends React.Component { 10 | static getStores() { 11 | return [ TodoStore ]; 12 | } 13 | 14 | static calculateState(prevState) { 15 | return { 16 | todos: TodoStore.getState(), 17 | }; 18 | } 19 | 20 | render() { 21 | return ( 22 | 28 | ); 29 | } 30 | } 31 | 32 | window.App.TodoListContainer = Container.create(TodoListContainer); 33 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/components/TodoListContainer.js: -------------------------------------------------------------------------------- 1 | const { Container } = FluxUtils; 2 | 3 | const { 4 | TodoActions, 5 | TodoStore, 6 | TodoList 7 | } = window.App; 8 | 9 | class TodoListContainer extends React.Component { 10 | static getStores() { 11 | return [ TodoStore ]; 12 | } 13 | 14 | static calculateState(prevState) { 15 | return { 16 | todos: TodoStore.getState(), 17 | }; 18 | } 19 | 20 | render() { 21 | return ( 22 | 28 | ); 29 | } 30 | } 31 | 32 | window.App.TodoListContainer = Container.create(TodoListContainer); 33 | -------------------------------------------------------------------------------- /level-24_react-redux/components/TodoListContainer.js: -------------------------------------------------------------------------------- 1 | const { connect } = ReactRedux; 2 | 3 | const { 4 | TodoActions, 5 | TodoList 6 | } = window.App; 7 | 8 | class TodoListContainer extends React.Component { 9 | render() { 10 | const { 11 | todos, 12 | updateTodo, 13 | toggleTodo, 14 | deleteTodo 15 | } = this.props; 16 | return ( 17 | 23 | ); 24 | } 25 | } 26 | 27 | window.App.TodoListContainer = connect( 28 | (state) => ({ todos: state.todos }), 29 | { 30 | updateTodo: TodoActions.updateTodo, 31 | toggleTodo: TodoActions.toggleTodo, 32 | deleteTodo: TodoActions.deleteTodo 33 | } 34 | )(TodoListContainer); 35 | -------------------------------------------------------------------------------- /level-25_immutablejs/components/TodoListContainer.js: -------------------------------------------------------------------------------- 1 | const { connect } = ReactRedux; 2 | 3 | const { 4 | TodoActions, 5 | TodoList 6 | } = window.App; 7 | 8 | class TodoListContainer extends React.Component { 9 | render() { 10 | const { 11 | todos, 12 | updateTodo, 13 | toggleTodo, 14 | deleteTodo 15 | } = this.props; 16 | return ( 17 | 23 | ); 24 | } 25 | } 26 | 27 | window.App.TodoListContainer = connect( 28 | (state) => ({ todos: state.todos }), 29 | { 30 | updateTodo: TodoActions.updateTodo, 31 | toggleTodo: TodoActions.toggleTodo, 32 | deleteTodo: TodoActions.deleteTodo 33 | } 34 | )(TodoListContainer); 35 | -------------------------------------------------------------------------------- /level-08_dynamic-children/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | InputField, 3 | TodoHeader, 4 | TodoList 5 | } = window.App; 6 | 7 | const todos = [ 8 | { 9 | id: 0, 10 | title: 'Item 1', 11 | completed: false 12 | }, 13 | { 14 | id: 1, 15 | title: 'Item 2', 16 | completed: false 17 | }, 18 | { 19 | id: 2, 20 | title: 'Item 3', 21 | completed: false 22 | } 23 | ]; 24 | 25 | class TodoApp extends React.Component { 26 | render() { 27 | return ( 28 |
    29 | !todo.completed).length} 33 | /> 34 | 35 | 36 |
    37 | ); 38 | } 39 | } 40 | 41 | window.App.TodoApp = TodoApp; 42 | -------------------------------------------------------------------------------- /level-17_container-pattern/components/TodoHeaderContainer.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoStore, 3 | TodoHeader 4 | } = window.App; 5 | 6 | class TodoHeaderContainer extends React.Component { 7 | constructor(props, context) { 8 | super(props, context); 9 | this.state = { todos: TodoStore.getAll() }; 10 | } 11 | 12 | componentDidMount() { 13 | this._removeChangeListener = TodoStore.addChangeListener( 14 | () => this.setState({ todos: TodoStore.getAll() }) 15 | ); 16 | } 17 | 18 | componentWillUnmount() { 19 | this._removeChangeListener(); 20 | } 21 | 22 | render() { 23 | return ( 24 | !todo.completed).length} 28 | /> 29 | ); 30 | } 31 | } 32 | 33 | window.App.TodoHeaderContainer = TodoHeaderContainer; 34 | -------------------------------------------------------------------------------- /level-17_container-pattern/components/TodoListContainer.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | TodoStore, 4 | TodoList 5 | } = window.App; 6 | 7 | class TodoListContainer extends React.Component { 8 | constructor(props, context) { 9 | super(props, context); 10 | this.state = { todos: TodoStore.getAll() }; 11 | } 12 | 13 | componentDidMount() { 14 | this._removeChangeListener = TodoStore.addChangeListener( 15 | () => this.setState({ todos: TodoStore.getAll() }) 16 | ); 17 | } 18 | 19 | componentWillUnmount() { 20 | this._removeChangeListener(); 21 | } 22 | 23 | render() { 24 | return ( 25 | 31 | ); 32 | } 33 | } 34 | 35 | window.App.TodoListContainer = TodoListContainer; 36 | -------------------------------------------------------------------------------- /level-24_react-redux/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { ActionTypes } = window.App; 2 | 3 | window.App.TodoActions = { 4 | loadTodos() { 5 | return (dispatch) => { 6 | fetch('./todos.json') 7 | .then((response) => response.json()) 8 | .then((todos) => dispatch({ 9 | type: ActionTypes.LOAD_TODOS_SUCCESS, 10 | todos 11 | })); 12 | }; 13 | }, 14 | createTodo(title) { 15 | return { 16 | type: ActionTypes.CREATE_TODO, 17 | title 18 | }; 19 | }, 20 | updateTodo(id, title) { 21 | return { 22 | type: ActionTypes.UPDATE_TODO, 23 | id, 24 | title 25 | }; 26 | }, 27 | toggleTodo(id, completed) { 28 | return { 29 | type: ActionTypes.TOGGLE_TODO, 30 | id, 31 | completed 32 | }; 33 | }, 34 | deleteTodo(id) { 35 | return { 36 | type: ActionTypes.DELETE_TODO, 37 | id 38 | }; 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /level-25_immutablejs/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { ActionTypes } = window.App; 2 | 3 | window.App.TodoActions = { 4 | loadTodos() { 5 | return (dispatch) => { 6 | fetch('./todos.json') 7 | .then((response) => response.json()) 8 | .then((todos) => dispatch({ 9 | type: ActionTypes.LOAD_TODOS_SUCCESS, 10 | todos 11 | })); 12 | }; 13 | }, 14 | createTodo(title) { 15 | return { 16 | type: ActionTypes.CREATE_TODO, 17 | title 18 | }; 19 | }, 20 | updateTodo(id, title) { 21 | return { 22 | type: ActionTypes.UPDATE_TODO, 23 | id, 24 | title 25 | }; 26 | }, 27 | toggleTodo(id, completed) { 28 | return { 29 | type: ActionTypes.TOGGLE_TODO, 30 | id, 31 | completed 32 | }; 33 | }, 34 | deleteTodo(id) { 35 | return { 36 | type: ActionTypes.DELETE_TODO, 37 | id 38 | }; 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /level-14_flux-actions/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | window.App.TodoActions = { 7 | loadTodos() { 8 | fetch('./todos.json') 9 | .then((response) => response.json()) 10 | .then((todos) => AppDispatcher.dispatch({ 11 | type: ActionTypes.LOAD_TODOS_SUCCESS, 12 | todos 13 | })); 14 | }, 15 | createTodo(title) { 16 | AppDispatcher.dispatch({ 17 | type: ActionTypes.CREATE_TODO, 18 | title 19 | }); 20 | }, 21 | updateTodo(id, title) { 22 | AppDispatcher.dispatch({ 23 | type: ActionTypes.UPDATE_TODO, 24 | id, 25 | title 26 | }); 27 | }, 28 | toggleTodo(id, completed) { 29 | AppDispatcher.dispatch({ 30 | type: ActionTypes.TOGGLE_TODO, 31 | id, 32 | completed 33 | }); 34 | }, 35 | deleteTodo(id) { 36 | AppDispatcher.dispatch({ 37 | type: ActionTypes.DELETE_TODO, 38 | id 39 | }); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /level-15_flux-stores/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | window.App.TodoActions = { 7 | loadTodos() { 8 | fetch('./todos.json') 9 | .then((response) => response.json()) 10 | .then((todos) => AppDispatcher.dispatch({ 11 | type: ActionTypes.LOAD_TODOS_SUCCESS, 12 | todos 13 | })); 14 | }, 15 | createTodo(title) { 16 | AppDispatcher.dispatch({ 17 | type: ActionTypes.CREATE_TODO, 18 | title 19 | }); 20 | }, 21 | updateTodo(id, title) { 22 | AppDispatcher.dispatch({ 23 | type: ActionTypes.UPDATE_TODO, 24 | id, 25 | title 26 | }); 27 | }, 28 | toggleTodo(id, completed) { 29 | AppDispatcher.dispatch({ 30 | type: ActionTypes.TOGGLE_TODO, 31 | id, 32 | completed 33 | }); 34 | }, 35 | deleteTodo(id) { 36 | AppDispatcher.dispatch({ 37 | type: ActionTypes.DELETE_TODO, 38 | id 39 | }); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /level-18_flux-utils/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | window.App.TodoActions = { 7 | loadTodos() { 8 | fetch('./todos.json') 9 | .then((response) => response.json()) 10 | .then((todos) => AppDispatcher.dispatch({ 11 | type: ActionTypes.LOAD_TODOS_SUCCESS, 12 | todos 13 | })); 14 | }, 15 | createTodo(title) { 16 | AppDispatcher.dispatch({ 17 | type: ActionTypes.CREATE_TODO, 18 | title 19 | }); 20 | }, 21 | updateTodo(id, title) { 22 | AppDispatcher.dispatch({ 23 | type: ActionTypes.UPDATE_TODO, 24 | id, 25 | title 26 | }); 27 | }, 28 | toggleTodo(id, completed) { 29 | AppDispatcher.dispatch({ 30 | type: ActionTypes.TOGGLE_TODO, 31 | id, 32 | completed 33 | }); 34 | }, 35 | deleteTodo(id) { 36 | AppDispatcher.dispatch({ 37 | type: ActionTypes.DELETE_TODO, 38 | id 39 | }); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /level-20_redux-reducers/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | window.App.TodoActions = { 7 | loadTodos() { 8 | fetch('./todos.json') 9 | .then((response) => response.json()) 10 | .then((todos) => AppDispatcher.dispatch({ 11 | type: ActionTypes.LOAD_TODOS_SUCCESS, 12 | todos 13 | })); 14 | }, 15 | createTodo(title) { 16 | AppDispatcher.dispatch({ 17 | type: ActionTypes.CREATE_TODO, 18 | title 19 | }); 20 | }, 21 | updateTodo(id, title) { 22 | AppDispatcher.dispatch({ 23 | type: ActionTypes.UPDATE_TODO, 24 | id, 25 | title 26 | }); 27 | }, 28 | toggleTodo(id, completed) { 29 | AppDispatcher.dispatch({ 30 | type: ActionTypes.TOGGLE_TODO, 31 | id, 32 | completed 33 | }); 34 | }, 35 | deleteTodo(id) { 36 | AppDispatcher.dispatch({ 37 | type: ActionTypes.DELETE_TODO, 38 | id 39 | }); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /level-21_redux-store/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | window.App.TodoActions = { 7 | loadTodos() { 8 | fetch('./todos.json') 9 | .then((response) => response.json()) 10 | .then((todos) => AppDispatcher.dispatch({ 11 | type: ActionTypes.LOAD_TODOS_SUCCESS, 12 | todos 13 | })); 14 | }, 15 | createTodo(title) { 16 | AppDispatcher.dispatch({ 17 | type: ActionTypes.CREATE_TODO, 18 | title 19 | }); 20 | }, 21 | updateTodo(id, title) { 22 | AppDispatcher.dispatch({ 23 | type: ActionTypes.UPDATE_TODO, 24 | id, 25 | title 26 | }); 27 | }, 28 | toggleTodo(id, completed) { 29 | AppDispatcher.dispatch({ 30 | type: ActionTypes.TOGGLE_TODO, 31 | id, 32 | completed 33 | }); 34 | }, 35 | deleteTodo(id) { 36 | AppDispatcher.dispatch({ 37 | type: ActionTypes.DELETE_TODO, 38 | id 39 | }); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /level-17_container-pattern/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | window.App.TodoActions = { 7 | loadTodos() { 8 | fetch('./todos.json') 9 | .then((response) => response.json()) 10 | .then((todos) => AppDispatcher.dispatch({ 11 | type: ActionTypes.LOAD_TODOS_SUCCESS, 12 | todos 13 | })); 14 | }, 15 | createTodo(title) { 16 | AppDispatcher.dispatch({ 17 | type: ActionTypes.CREATE_TODO, 18 | title 19 | }); 20 | }, 21 | updateTodo(id, title) { 22 | AppDispatcher.dispatch({ 23 | type: ActionTypes.UPDATE_TODO, 24 | id, 25 | title 26 | }); 27 | }, 28 | toggleTodo(id, completed) { 29 | AppDispatcher.dispatch({ 30 | type: ActionTypes.TOGGLE_TODO, 31 | id, 32 | completed 33 | }); 34 | }, 35 | deleteTodo(id) { 36 | AppDispatcher.dispatch({ 37 | type: ActionTypes.DELETE_TODO, 38 | id 39 | }); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | window.App.TodoActions = { 7 | loadTodos() { 8 | fetch('./todos.json') 9 | .then((response) => response.json()) 10 | .then((todos) => AppDispatcher.dispatch({ 11 | type: ActionTypes.LOAD_TODOS_SUCCESS, 12 | todos 13 | })); 14 | }, 15 | createTodo(title) { 16 | AppDispatcher.dispatch({ 17 | type: ActionTypes.CREATE_TODO, 18 | title 19 | }); 20 | }, 21 | updateTodo(id, title) { 22 | AppDispatcher.dispatch({ 23 | type: ActionTypes.UPDATE_TODO, 24 | id, 25 | title 26 | }); 27 | }, 28 | toggleTodo(id, completed) { 29 | AppDispatcher.dispatch({ 30 | type: ActionTypes.TOGGLE_TODO, 31 | id, 32 | completed 33 | }); 34 | }, 35 | deleteTodo(id) { 36 | AppDispatcher.dispatch({ 37 | type: ActionTypes.DELETE_TODO, 38 | id 39 | }); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /level-10_forms/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /level-06_transferring-props/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /level-08_dynamic-children/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /level-09_stateful-component/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /level-05_component-composition/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /level-07_prop-types-n-default-values/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /level-25_immutablejs/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.object.isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-10_forms/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-11_component-lifecycle/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-15_flux-stores/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-18_flux-utils/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-21_redux-store/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-24_react-redux/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-13_flux-dispatcher/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-14_flux-actions/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-20_redux-reducers/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-22_redux-actions/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-17_container-pattern/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/components/TodoList.js: -------------------------------------------------------------------------------- 1 | const { TodoItem } = window.App; 2 | 3 | class TodoList extends React.Component { 4 | render() { 5 | const { 6 | todos, 7 | onUpdateTodo, 8 | onToggleTodo, 9 | onDeleteTodo 10 | } = this.props; 11 | const todoElements = todos.map((todo) => ( 12 |
  • 13 | onUpdateTodo && onUpdateTodo(todo.id, content)} 17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)} 18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)} 19 | /> 20 |
  • 21 | )); 22 | return
      {todoElements}
    ; 23 | } 24 | } 25 | 26 | TodoList.propTypes = { 27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 28 | onUpdateTodo: React.PropTypes.func, 29 | onToggleTodo: React.PropTypes.func, 30 | onDeleteTodo: React.PropTypes.func 31 | }; 32 | 33 | window.App.TodoList = TodoList; 34 | -------------------------------------------------------------------------------- /level-11_component-lifecycle/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 26 | 27 | -------------------------------------------------------------------------------- /level-10_forms/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-11_component-lifecycle/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-15_flux-stores/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-18_flux-utils/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-21_redux-store/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-24_react-redux/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-25_immutablejs/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-13_flux-dispatcher/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-14_flux-actions/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-20_redux-reducers/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-22_redux-actions/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-17_container-pattern/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/components/InputField.js: -------------------------------------------------------------------------------- 1 | class InputField extends React.Component { 2 | constructor(props, context) { 3 | super(props, context); 4 | this.state = { value: props.value || '' }; 5 | this.handleChange = this.handleChange.bind(this); 6 | this.handleKeyDown = this.handleKeyDown.bind(this); 7 | } 8 | 9 | handleChange(e) { 10 | this.setState({ value: e.target.value }); 11 | } 12 | 13 | handleKeyDown(e) { 14 | const { 15 | onKeyDown, 16 | onSubmitEditing 17 | } = this.props; 18 | const { value } = this.state; 19 | switch (e.keyCode) { 20 | case 13: 21 | if (value.trim()) { 22 | onSubmitEditing && onSubmitEditing(value); 23 | } 24 | this.setState({ value: '' }); 25 | break; 26 | } 27 | onKeyDown && onKeyDown(e); 28 | } 29 | 30 | render() { 31 | return ( 32 | 39 | ); 40 | } 41 | } 42 | 43 | InputField.propTypes = { 44 | onSubmitEditing: React.PropTypes.func 45 | }; 46 | 47 | window.App.InputField = InputField; 48 | -------------------------------------------------------------------------------- /level-01_react/README.md: -------------------------------------------------------------------------------- 1 | # Level 1. 用元件思維設計應用程式 2 | 3 | 歡迎來到「24 小時,React 快速入門」系列教學 :mortar_board: Level 1 ~! 4 | > :bowtie::Wish you have a happy learning! 5 | 6 | 7 | ## :checkered_flag: 關卡目標 8 | 9 | 1. 完成主線任務: 10 | 1. 列出 TodoApp 的功能清單 11 | 2. 使用「元件思維」設計 TodoApp 12 | 2. 習得心法:打通任督二脈,了解「**元件思維**」 13 | 14 | 15 | ## :triangular_flag_on_post: 主線任務 16 | 17 | ### 1. 列出 TodoApp 的功能清單 18 | 19 | 相信大家都有用過市面上的 TodoApp,一個簡易的 TodoApp 會有以下功能: 20 | 21 | 1. 列出所有待辦項目 22 | 2. 提示待辦數量 23 | 3. 新增待辦項目 24 | 4. 編輯待辦項目 25 | 5. 刪除待辦項目 26 | 6. 切換項目處理狀態 27 | 28 | 根據以上功能,我們畫出第一版 wireframe: 29 | 30 | ![TodoApp markup](../assets/todoapp-markup.png) 31 | 32 | ### 2. 使用元件思維,設計 TodoApp 33 | 34 | > :neckbeard::少年,我看你天資聰穎,是萬中選一的前端人,我這有一篇...[祕笈](https://medium.com/p/ab93203f6c53),替你打通元件思維的任督二脈,你斟酌看看 :lollipop: 35 | 36 | 將上一步的 UI 劃分成多個元件,並且替它們取上名稱: 37 | 38 | ![TodoApp components](../assets/todoapp-components.png) 39 | 40 | > :bowtie::在這一階段我們必須盡可能得讓元件可以**重複利用**;思考邏輯是這樣的 - 因為 TodoList 中每一行待辦項目的 UI 都是相同的,僅顯示的資料不一樣,所以我們將待辦項目拉成一個 TodoItem 元件。 41 | 42 | 43 | ## :rocket: 44 | 45 | | [主頁](../../../) | [下一關. 建置簡易的開發環境](../level-02_initial-project) | 46 | 47 | | :raising_hand: [我要提問](https://github.com/shiningjason1989/react-quick-tutorial/issues/new) | 48 | 49 | 50 | ![Analytics](https://shining-ga-beacon.appspot.com/UA-77436651-1/level-01_react?pixel) 51 | -------------------------------------------------------------------------------- /level-13_flux-dispatcher/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 29 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | TodoActions, 3 | TodoStore, 4 | InputField, 5 | TodoHeader, 6 | TodoList 7 | } = window.App; 8 | 9 | class TodoApp extends React.Component { 10 | constructor(props, context) { 11 | super(props, context); 12 | this.state = { 13 | todos: TodoStore.getAll() 14 | }; 15 | } 16 | 17 | componentDidMount() { 18 | TodoActions.loadTodos(); 19 | this._removeChangeListener = TodoStore.addChangeListener( 20 | () => this.setState({ todos: TodoStore.getAll() }) 21 | ); 22 | } 23 | 24 | componentWillUnmount() { 25 | this._removeChangeListener(); 26 | } 27 | 28 | render() { 29 | const { todos } = this.state; 30 | return ( 31 |
    32 | !todo.completed).length} 36 | /> 37 | 41 | 47 |
    48 | ); 49 | } 50 | } 51 | 52 | window.App.TodoApp = TodoApp; 53 | -------------------------------------------------------------------------------- /level-09_stateful-component/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | InputField, 3 | TodoHeader, 4 | TodoList 5 | } = window.App; 6 | 7 | const _deleteTodo = (todos, id) => { 8 | const idx = todos.findIndex((todo) => todo.id === id); 9 | if (idx !== -1) todos.splice(idx, 1); 10 | return todos; 11 | }; 12 | 13 | class TodoApp extends React.Component { 14 | constructor(props, context) { 15 | super(props, context); 16 | this.state = { 17 | todos: [ 18 | { 19 | id: 0, 20 | title: 'Item 1', 21 | completed: false 22 | }, 23 | { 24 | id: 1, 25 | title: 'Item 2', 26 | completed: false 27 | }, 28 | { 29 | id: 2, 30 | title: 'Item 3', 31 | completed: false 32 | } 33 | ] 34 | }; 35 | } 36 | 37 | render() { 38 | const { todos } = this.state; 39 | return ( 40 |
    41 | !todo.completed).length} 45 | /> 46 | 47 | this.setState({ 51 | todos: _deleteTodo(todos, ...args) 52 | }) 53 | } 54 | /> 55 |
    56 | ); 57 | } 58 | } 59 | 60 | window.App.TodoApp = TodoApp; 61 | -------------------------------------------------------------------------------- /level-14_flux-actions/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | -------------------------------------------------------------------------------- /level-25_immutablejs/reducers/todos.js: -------------------------------------------------------------------------------- 1 | const { List, Record } = Immutable; 2 | 3 | const { ActionTypes } = window.App; 4 | 5 | const TodoRecord = Record({ 6 | id: undefined, 7 | title: undefined, 8 | completed: false 9 | }); 10 | 11 | const _findIdxById = (todos, id) => todos.findIndex((todo) => todo.id === id); 12 | 13 | const _createTodo = (todos, title) => 14 | todos.push(new TodoRecord({ 15 | id: todos.last().id + 1, 16 | title, 17 | completed: false 18 | })); 19 | 20 | const _updateTodo = (todos, id, title) => 21 | todos.setIn([ _findIdxById(todos, id), 'title' ], title); 22 | 23 | const _toggleTodo = (todos, id, completed) => 24 | todos.setIn([ _findIdxById(todos, id), 'completed' ], completed); 25 | 26 | const _deleteTodo = (todos, id) => 27 | todos.delete(_findIdxById(todos, id)); 28 | 29 | window.App.reducers.todos = (state = new List(), action) => { 30 | switch (action.type) { 31 | case ActionTypes.LOAD_TODOS_SUCCESS: 32 | return new List(action.todos).map((todo) => new TodoRecord(todo)); 33 | case ActionTypes.CREATE_TODO: 34 | return _createTodo(state, action.title); 35 | case ActionTypes.UPDATE_TODO: 36 | return _updateTodo(state, action.id, action.title); 37 | case ActionTypes.TOGGLE_TODO: 38 | return _toggleTodo(state, action.id, action.completed); 39 | case ActionTypes.DELETE_TODO: 40 | return _deleteTodo(state, action.id); 41 | default: 42 | return state; 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /level-09_stateful-component/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onDelete 19 | } = this.props; 20 | return ( 21 |
    22 | 23 | {title} 24 | 25 |
    26 | ); 27 | } 28 | 29 | renderEditMode() { 30 | const { title } = this.props; 31 | return ( 32 | { 38 | if (e.keyCode === 27) { 39 | e.preventDefault(); 40 | this.toggleEditMode(); 41 | } 42 | }} 43 | /> 44 | ); 45 | } 46 | 47 | render() { 48 | return this.state.editable ? 49 | this.renderEditMode() : 50 | this.renderViewMode(); 51 | } 52 | } 53 | 54 | TodoItem.propTypes = { 55 | title: React.PropTypes.string.isRequired, 56 | completed: React.PropTypes.bool.isRequired, 57 | onDelete: React.PropTypes.func 58 | }; 59 | 60 | window.App.TodoItem = TodoItem; 61 | -------------------------------------------------------------------------------- /level-15_flux-stores/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | -------------------------------------------------------------------------------- /level-12_flux/README.md: -------------------------------------------------------------------------------- 1 | # Level 12. 深入淺出 Flux 2 | 3 | 歡迎來到「24 小時,React 快速入門」系列教學 :mortar_board: Level 12 ~! 4 | > :bowtie::Wish you have a happy learning! 5 | 6 | 7 | ## :checkered_flag: 關卡目標 8 | 9 | 1. 習得心法: 10 | 1. 理解 Flux 是什麼,如何用一句話定義 Flux 11 | 2. 清楚 Flux 中每個角色所扮演的職責和互動方式 12 | 3. 認識 Flux 的運作流程 13 | 14 | 15 | ## :triangular_flag_on_post: 打通 Flux 的任督二脈 16 | 17 | > :neckbeard::少年,我看你天資聰穎,是萬中選一的前端人,我這有一篇...[祕笈](https://medium.com/p/44a48c320e11),幫你深入淺出 Flux 的武功心法,你斟酌看看 :lollipop: 18 | 19 | ![Flux](../assets/flux-diagram.png) 20 | 21 | 上方是 Facebook 提供的 Flux 架構圖,要確認你是否吸收了 Flux 精髓,你可以詢問自己以下幾個問題: 22 | 23 | 1. ***Flux 是什麼,可以幫助你什麼(試著用一句話定義)*** 24 | 2. ***Flux 主要的四個角色是誰,他們的職責是什麼*** 25 | 3. ***看著圖,簡述 Flux 的運作流程*** 26 | 27 | 如果你認為自己的回答還有地方卡卡的,可以 28 | 29 | 1. 再讀一次[祕笈](https://medium.com/p/44a48c320e11) 30 | 2. 閱讀下方其他大神的見解 31 | 3. 直接進入下一關,從 code 學習 32 | 33 | 最後,再次回答上面的問題,驗證所學:) 34 | 35 | > :bowtie::Good Luck! :four_leaf_clover: 也非常歡迎你開 issue 直接詢問我 ^ ^ 36 | 37 | ###### 參考連結 38 | 39 | - [Flux For Stupid People](http://blog.andrewray.me/flux-for-stupid-people/) 40 | - [A cartoon guide to Flux](https://code-cartoons.com/a-cartoon-guide-to-flux-6157355ab207) 41 | - [The Case for Flux](https://medium.com/swlh/the-case-for-flux-379b7d1982c6) 42 | - [從 Flux 與 MVC 的差異來簡介 Flux](http://blog.techbridge.cc/2016/04/29/introduce-flux-from-flux-and-mvc/) 43 | 44 | 45 | ## :rocket: 46 | 47 | | [主頁](../../../) | [上一關](../level-11_component-lifecycle) | [下一關. 完成 Dispatcher:Flux 最重要的角色](../level-13_flux-dispatcher) | 48 | 49 | | :raising_hand: [我要提問](https://github.com/shiningjason1989/react-quick-tutorial/issues/new) | 50 | 51 | 52 | ![Analytics](https://shining-ga-beacon.appspot.com/UA-77436651-1/level-12_flux?pixel) 53 | -------------------------------------------------------------------------------- /level-20_redux-reducers/reducers/todos.js: -------------------------------------------------------------------------------- 1 | const { ActionTypes } = window.App; 2 | 3 | const _createTodo = (todos, title) => { 4 | return [ 5 | ...todos, 6 | { 7 | id: todos[todos.length - 1].id + 1, 8 | title, 9 | completed: false 10 | } 11 | ]; 12 | }; 13 | 14 | const _updateTodo = (todos, id, title) => { 15 | const idx = todos.findIndex((todo) => todo.id === id); 16 | if (idx === -1) return todos; 17 | 18 | const newTodos = [ ...todos ]; 19 | newTodos[idx] = { 20 | ...todos[idx], 21 | title 22 | }; 23 | return newTodos; 24 | }; 25 | 26 | const _toggleTodo = (todos, id, completed) => { 27 | const idx = todos.findIndex((todo) => todo.id === id); 28 | if (idx === -1) return todos; 29 | 30 | const newTodos = [ ...todos ]; 31 | newTodos[idx] = { 32 | ...todos[idx], 33 | completed 34 | }; 35 | return newTodos; 36 | }; 37 | 38 | const _deleteTodo = (todos, id) => { 39 | const idx = todos.findIndex((todo) => todo.id === id); 40 | if (idx === -1) return todos; 41 | 42 | const newTodos = [ ...todos ]; 43 | newTodos.splice(idx, 1); 44 | return newTodos; 45 | }; 46 | 47 | window.App.reducers.todos = (state = [], action) => { 48 | switch (action.type) { 49 | case ActionTypes.LOAD_TODOS_SUCCESS: 50 | return action.todos; 51 | case ActionTypes.CREATE_TODO: 52 | return _createTodo(state, action.title); 53 | case ActionTypes.UPDATE_TODO: 54 | return _updateTodo(state, action.id, action.title); 55 | case ActionTypes.TOGGLE_TODO: 56 | return _toggleTodo(state, action.id, action.completed); 57 | case ActionTypes.DELETE_TODO: 58 | return _deleteTodo(state, action.id); 59 | default: 60 | return state; 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /level-21_redux-store/reducers/todos.js: -------------------------------------------------------------------------------- 1 | const { ActionTypes } = window.App; 2 | 3 | const _createTodo = (todos, title) => { 4 | return [ 5 | ...todos, 6 | { 7 | id: todos[todos.length - 1].id + 1, 8 | title, 9 | completed: false 10 | } 11 | ]; 12 | }; 13 | 14 | const _updateTodo = (todos, id, title) => { 15 | const idx = todos.findIndex((todo) => todo.id === id); 16 | if (idx === -1) return todos; 17 | 18 | const newTodos = [ ...todos ]; 19 | newTodos[idx] = { 20 | ...todos[idx], 21 | title 22 | }; 23 | return newTodos; 24 | }; 25 | 26 | const _toggleTodo = (todos, id, completed) => { 27 | const idx = todos.findIndex((todo) => todo.id === id); 28 | if (idx === -1) return todos; 29 | 30 | const newTodos = [ ...todos ]; 31 | newTodos[idx] = { 32 | ...todos[idx], 33 | completed 34 | }; 35 | return newTodos; 36 | }; 37 | 38 | const _deleteTodo = (todos, id) => { 39 | const idx = todos.findIndex((todo) => todo.id === id); 40 | if (idx === -1) return todos; 41 | 42 | const newTodos = [ ...todos ]; 43 | newTodos.splice(idx, 1); 44 | return newTodos; 45 | }; 46 | 47 | window.App.reducers.todos = (state = [], action) => { 48 | switch (action.type) { 49 | case ActionTypes.LOAD_TODOS_SUCCESS: 50 | return action.todos; 51 | case ActionTypes.CREATE_TODO: 52 | return _createTodo(state, action.title); 53 | case ActionTypes.UPDATE_TODO: 54 | return _updateTodo(state, action.id, action.title); 55 | case ActionTypes.TOGGLE_TODO: 56 | return _toggleTodo(state, action.id, action.completed); 57 | case ActionTypes.DELETE_TODO: 58 | return _deleteTodo(state, action.id); 59 | default: 60 | return state; 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /level-22_redux-actions/reducers/todos.js: -------------------------------------------------------------------------------- 1 | const { ActionTypes } = window.App; 2 | 3 | const _createTodo = (todos, title) => { 4 | return [ 5 | ...todos, 6 | { 7 | id: todos[todos.length - 1].id + 1, 8 | title, 9 | completed: false 10 | } 11 | ]; 12 | }; 13 | 14 | const _updateTodo = (todos, id, title) => { 15 | const idx = todos.findIndex((todo) => todo.id === id); 16 | if (idx === -1) return todos; 17 | 18 | const newTodos = [ ...todos ]; 19 | newTodos[idx] = { 20 | ...todos[idx], 21 | title 22 | }; 23 | return newTodos; 24 | }; 25 | 26 | const _toggleTodo = (todos, id, completed) => { 27 | const idx = todos.findIndex((todo) => todo.id === id); 28 | if (idx === -1) return todos; 29 | 30 | const newTodos = [ ...todos ]; 31 | newTodos[idx] = { 32 | ...todos[idx], 33 | completed 34 | }; 35 | return newTodos; 36 | }; 37 | 38 | const _deleteTodo = (todos, id) => { 39 | const idx = todos.findIndex((todo) => todo.id === id); 40 | if (idx === -1) return todos; 41 | 42 | const newTodos = [ ...todos ]; 43 | newTodos.splice(idx, 1); 44 | return newTodos; 45 | }; 46 | 47 | window.App.reducers.todos = (state = [], action) => { 48 | switch (action.type) { 49 | case ActionTypes.LOAD_TODOS_SUCCESS: 50 | return action.todos; 51 | case ActionTypes.CREATE_TODO: 52 | return _createTodo(state, action.title); 53 | case ActionTypes.UPDATE_TODO: 54 | return _updateTodo(state, action.id, action.title); 55 | case ActionTypes.TOGGLE_TODO: 56 | return _toggleTodo(state, action.id, action.completed); 57 | case ActionTypes.DELETE_TODO: 58 | return _deleteTodo(state, action.id); 59 | default: 60 | return state; 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /level-24_react-redux/reducers/todos.js: -------------------------------------------------------------------------------- 1 | const { ActionTypes } = window.App; 2 | 3 | const _createTodo = (todos, title) => { 4 | return [ 5 | ...todos, 6 | { 7 | id: todos[todos.length - 1].id + 1, 8 | title, 9 | completed: false 10 | } 11 | ]; 12 | }; 13 | 14 | const _updateTodo = (todos, id, title) => { 15 | const idx = todos.findIndex((todo) => todo.id === id); 16 | if (idx === -1) return todos; 17 | 18 | const newTodos = [ ...todos ]; 19 | newTodos[idx] = { 20 | ...todos[idx], 21 | title 22 | }; 23 | return newTodos; 24 | }; 25 | 26 | const _toggleTodo = (todos, id, completed) => { 27 | const idx = todos.findIndex((todo) => todo.id === id); 28 | if (idx === -1) return todos; 29 | 30 | const newTodos = [ ...todos ]; 31 | newTodos[idx] = { 32 | ...todos[idx], 33 | completed 34 | }; 35 | return newTodos; 36 | }; 37 | 38 | const _deleteTodo = (todos, id) => { 39 | const idx = todos.findIndex((todo) => todo.id === id); 40 | if (idx === -1) return todos; 41 | 42 | const newTodos = [ ...todos ]; 43 | newTodos.splice(idx, 1); 44 | return newTodos; 45 | }; 46 | 47 | window.App.reducers.todos = (state = [], action) => { 48 | switch (action.type) { 49 | case ActionTypes.LOAD_TODOS_SUCCESS: 50 | return action.todos; 51 | case ActionTypes.CREATE_TODO: 52 | return _createTodo(state, action.title); 53 | case ActionTypes.UPDATE_TODO: 54 | return _updateTodo(state, action.id, action.title); 55 | case ActionTypes.TOGGLE_TODO: 56 | return _toggleTodo(state, action.id, action.completed); 57 | case ActionTypes.DELETE_TODO: 58 | return _deleteTodo(state, action.id); 59 | default: 60 | return state; 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/reducers/todos.js: -------------------------------------------------------------------------------- 1 | const { ActionTypes } = window.App; 2 | 3 | const _createTodo = (todos, title) => { 4 | return [ 5 | ...todos, 6 | { 7 | id: todos[todos.length - 1].id + 1, 8 | title, 9 | completed: false 10 | } 11 | ]; 12 | }; 13 | 14 | const _updateTodo = (todos, id, title) => { 15 | const idx = todos.findIndex((todo) => todo.id === id); 16 | if (idx === -1) return todos; 17 | 18 | const newTodos = [ ...todos ]; 19 | newTodos[idx] = { 20 | ...todos[idx], 21 | title 22 | }; 23 | return newTodos; 24 | }; 25 | 26 | const _toggleTodo = (todos, id, completed) => { 27 | const idx = todos.findIndex((todo) => todo.id === id); 28 | if (idx === -1) return todos; 29 | 30 | const newTodos = [ ...todos ]; 31 | newTodos[idx] = { 32 | ...todos[idx], 33 | completed 34 | }; 35 | return newTodos; 36 | }; 37 | 38 | const _deleteTodo = (todos, id) => { 39 | const idx = todos.findIndex((todo) => todo.id === id); 40 | if (idx === -1) return todos; 41 | 42 | const newTodos = [ ...todos ]; 43 | newTodos.splice(idx, 1); 44 | return newTodos; 45 | }; 46 | 47 | window.App.reducers.todos = (state = [], action) => { 48 | switch (action.type) { 49 | case ActionTypes.LOAD_TODOS_SUCCESS: 50 | return action.todos; 51 | case ActionTypes.CREATE_TODO: 52 | return _createTodo(state, action.title); 53 | case ActionTypes.UPDATE_TODO: 54 | return _updateTodo(state, action.id, action.title); 55 | case ActionTypes.TOGGLE_TODO: 56 | return _toggleTodo(state, action.id, action.completed); 57 | case ActionTypes.DELETE_TODO: 58 | return _deleteTodo(state, action.id); 59 | default: 60 | return state; 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /level-24_react-redux/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /level-25_immutablejs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /level-22_redux-actions/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | window.App.TodoActions = { 7 | loadTodos() { 8 | fetch('./todos.json') 9 | .then((response) => response.json()) 10 | .then((todos) => AppDispatcher.dispatch({ 11 | type: ActionTypes.LOAD_TODOS_SUCCESS, 12 | todos 13 | })); 14 | }, 15 | createTodo(title) { 16 | AppDispatcher.dispatch({ 17 | type: ActionTypes.CREATE_TODO, 18 | title 19 | }); 20 | }, 21 | updateTodo(id, title) { 22 | AppDispatcher.dispatch({ 23 | type: ActionTypes.UPDATE_TODO, 24 | id, 25 | title 26 | }); 27 | }, 28 | toggleTodo(id, completed) { 29 | AppDispatcher.dispatch({ 30 | type: ActionTypes.TOGGLE_TODO, 31 | id, 32 | completed 33 | }); 34 | }, 35 | deleteTodo(id) { 36 | AppDispatcher.dispatch({ 37 | type: ActionTypes.DELETE_TODO, 38 | id 39 | }); 40 | } 41 | }; 42 | 43 | window.App.TodoReduxActions = { 44 | loadTodos() { 45 | return (dispatch) => { 46 | fetch('./todos.json') 47 | .then((response) => response.json()) 48 | .then((todos) => dispatch({ 49 | type: ActionTypes.LOAD_TODOS_SUCCESS, 50 | todos 51 | })); 52 | }; 53 | }, 54 | createTodo(title) { 55 | return { 56 | type: ActionTypes.CREATE_TODO, 57 | title 58 | }; 59 | }, 60 | updateTodo(id, title) { 61 | return { 62 | type: ActionTypes.UPDATE_TODO, 63 | id, 64 | title 65 | }; 66 | }, 67 | toggleTodo(id, completed) { 68 | return { 69 | type: ActionTypes.TOGGLE_TODO, 70 | id, 71 | completed 72 | }; 73 | }, 74 | deleteTodo(id) { 75 | return { 76 | type: ActionTypes.DELETE_TODO, 77 | id 78 | }; 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | window.App.TodoActions = { 7 | loadTodos() { 8 | fetch('./todos.json') 9 | .then((response) => response.json()) 10 | .then((todos) => AppDispatcher.dispatch({ 11 | type: ActionTypes.LOAD_TODOS_SUCCESS, 12 | todos 13 | })); 14 | }, 15 | createTodo(title) { 16 | AppDispatcher.dispatch({ 17 | type: ActionTypes.CREATE_TODO, 18 | title 19 | }); 20 | }, 21 | updateTodo(id, title) { 22 | AppDispatcher.dispatch({ 23 | type: ActionTypes.UPDATE_TODO, 24 | id, 25 | title 26 | }); 27 | }, 28 | toggleTodo(id, completed) { 29 | AppDispatcher.dispatch({ 30 | type: ActionTypes.TOGGLE_TODO, 31 | id, 32 | completed 33 | }); 34 | }, 35 | deleteTodo(id) { 36 | AppDispatcher.dispatch({ 37 | type: ActionTypes.DELETE_TODO, 38 | id 39 | }); 40 | } 41 | }; 42 | 43 | window.App.TodoReduxActions = { 44 | loadTodos() { 45 | return (dispatch) => { 46 | fetch('./todos.json') 47 | .then((response) => response.json()) 48 | .then((todos) => dispatch({ 49 | type: ActionTypes.LOAD_TODOS_SUCCESS, 50 | todos 51 | })); 52 | }; 53 | }, 54 | createTodo(title) { 55 | return { 56 | type: ActionTypes.CREATE_TODO, 57 | title 58 | }; 59 | }, 60 | updateTodo(id, title) { 61 | return { 62 | type: ActionTypes.UPDATE_TODO, 63 | id, 64 | title 65 | }; 66 | }, 67 | toggleTodo(id, completed) { 68 | return { 69 | type: ActionTypes.TOGGLE_TODO, 70 | id, 71 | completed 72 | }; 73 | }, 74 | deleteTodo(id) { 75 | return { 76 | type: ActionTypes.DELETE_TODO, 77 | id 78 | }; 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /level-17_container-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 35 | 36 | -------------------------------------------------------------------------------- /level-10_forms/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-11_component-lifecycle/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-14_flux-actions/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-15_flux-stores/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-18_flux-utils/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-21_redux-store/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-22_redux-actions/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-24_react-redux/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-25_immutablejs/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-13_flux-dispatcher/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-17_container-pattern/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-20_redux-reducers/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/components/TodoItem.js: -------------------------------------------------------------------------------- 1 | const { InputField } = window.App; 2 | 3 | class TodoItem extends React.Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | this.state = { editable: false }; 7 | this.toggleEditMode = this.toggleEditMode.bind(this); 8 | } 9 | 10 | toggleEditMode() { 11 | this.setState({ editable: !this.state.editable }); 12 | } 13 | 14 | renderViewMode() { 15 | const { 16 | title, 17 | completed, 18 | onToggle, 19 | onDelete 20 | } = this.props; 21 | return ( 22 |
    23 | onToggle && onToggle(!completed)} 27 | /> 28 | {title} 29 | 30 |
    31 | ); 32 | } 33 | 34 | renderEditMode() { 35 | const { title, onUpdate } = this.props; 36 | return ( 37 | { 43 | if (e.keyCode === 27) { 44 | e.preventDefault(); 45 | this.toggleEditMode(); 46 | } 47 | }} 48 | onSubmitEditing={(content) => { 49 | onUpdate && onUpdate(content); 50 | this.toggleEditMode(); 51 | }} 52 | /> 53 | ); 54 | } 55 | 56 | render() { 57 | return this.state.editable ? 58 | this.renderEditMode() : 59 | this.renderViewMode(); 60 | } 61 | } 62 | 63 | TodoItem.propTypes = { 64 | title: React.PropTypes.string.isRequired, 65 | completed: React.PropTypes.bool.isRequired, 66 | onUpdate: React.PropTypes.func, 67 | onToggle: React.PropTypes.func, 68 | onDelete: React.PropTypes.func 69 | }; 70 | 71 | window.App.TodoItem = TodoItem; 72 | -------------------------------------------------------------------------------- /level-18_flux-utils/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /level-18_flux-utils/stores/TodoStore.js: -------------------------------------------------------------------------------- 1 | const { ReduceStore } = FluxUtils; 2 | 3 | const { 4 | ActionTypes, 5 | AppDispatcher 6 | } = window.App; 7 | 8 | const _createTodo = (todos, title) => { 9 | return [ 10 | ...todos, 11 | { 12 | id: todos[todos.length - 1].id + 1, 13 | title, 14 | completed: false 15 | } 16 | ]; 17 | }; 18 | 19 | const _updateTodo = (todos, id, title) => { 20 | const idx = todos.findIndex((todo) => todo.id === id); 21 | if (idx === -1) return todos; 22 | 23 | const newTodos = [ ...todos ]; 24 | newTodos[idx] = { 25 | ...todos[idx], 26 | title 27 | }; 28 | return newTodos; 29 | }; 30 | 31 | const _toggleTodo = (todos, id, completed) => { 32 | const idx = todos.findIndex((todo) => todo.id === id); 33 | if (idx === -1) return todos; 34 | 35 | const newTodos = [ ...todos ]; 36 | newTodos[idx] = { 37 | ...todos[idx], 38 | completed 39 | }; 40 | return newTodos; 41 | }; 42 | 43 | const _deleteTodo = (todos, id) => { 44 | const idx = todos.findIndex((todo) => todo.id === id); 45 | if (idx === -1) return todos; 46 | 47 | const newTodos = [ ...todos ]; 48 | newTodos.splice(idx, 1); 49 | return newTodos; 50 | }; 51 | 52 | class TodoStore extends ReduceStore { 53 | getInitialState() { 54 | return []; 55 | } 56 | 57 | reduce(state, action) { 58 | switch (action.type) { 59 | case ActionTypes.LOAD_TODOS_SUCCESS: 60 | return action.todos; 61 | case ActionTypes.CREATE_TODO: 62 | return _createTodo(state, action.title); 63 | case ActionTypes.UPDATE_TODO: 64 | return _updateTodo(state, action.id, action.title); 65 | case ActionTypes.TOGGLE_TODO: 66 | return _toggleTodo(state, action.id, action.completed); 67 | case ActionTypes.DELETE_TODO: 68 | return _deleteTodo(state, action.id); 69 | default: 70 | return state; 71 | } 72 | } 73 | } 74 | 75 | window.App.TodoStore = new TodoStore(AppDispatcher); 76 | -------------------------------------------------------------------------------- /level-19_redux/README.md: -------------------------------------------------------------------------------- 1 | # Level 19. 深入淺出 Redux 2 | 3 | 歡迎來到「24 小時,React 快速入門」系列教學 :mortar_board: Level 19 ~! 4 | > :bowtie::Wish you have a happy learning! 5 | 6 | 7 | ## :checkered_flag: 關卡目標 8 | 9 | 1. 習得心法: 10 | 1. 理解 Redux 是什麼,如何用一句話定義 Redux 11 | 2. 清楚 Redux 和 Flux 的差別 12 | 3. 認識 Redux 的運作流程 13 | 14 | 15 | ## :triangular_flag_on_post: 打通 Redux 的任督二脈 16 | 17 | > :neckbeard::少年,我看你天資聰穎,是萬中選一的前端人,我這有一篇...[祕笈](https://medium.com/p/7b08403c4957),幫你深入淺出 Redux 的武功心法,你斟酌看看 :lollipop: 18 | 19 | ![Redux](../assets/redux-diagram.png) 20 | 21 | 上方是 Redux 架構圖,要確認你是否吸收了 Redux 精髓,你可以詢問自己以下幾個問題: 22 | 23 | 1. ***Redux 是什麼,可以幫助你什麼(試著用一句話定義)*** 24 | 2. ***Redux 和 Flux 的差別是什麼*** 25 | 3. ***看著圖,簡述 Redux 的運作流程*** 26 | 27 | 如果你認為自己的回答還有地方卡卡的,可以 28 | 29 | 1. 再讀一次[祕笈](https://medium.com/p/7b08403c4957) 30 | 2. 閱讀下方其他大神的見解 31 | 3. 直接進入下一關,從 code 學習 32 | 33 | 最後,再次回答上面的問題,驗證所學:) 34 | 35 | > :bowtie::Good Luck! :four_leaf_clover: 也非常歡迎你開 issue 直接詢問我 ^ ^ 36 | 37 | ###### 參考連結 38 | 39 | - [An Introduction To Redux](https://www.smashingmagazine.com/2016/06/an-introduction-to-redux/) 40 | - [A Cartoon Intro To Redux](https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6) 41 | - [Redux 初探](https://www.facebook.com/notes/%E9%99%B8%E6%8C%AF%E6%81%A9/redux%E5%88%9D%E6%8E%A2/1025850617451561) 42 | - [Dan Abramov 解釋 Predictable State Container](https://hashnode.com/post/how-do-you-explain-the-term-predictable-state-container-in-simple-words-ciizdac5300wege53dogz8aqk) 43 | - [Dan Abramov’s React Europe 2015 talk](https://www.youtube.com/watch?v=xsSnOQynTHs) 44 | - [Dan Abramov’s React Europe 2016 talk](https://www.youtube.com/watch?v=uvAXVMwHJXU) 45 | 46 | 47 | ## :rocket: 48 | 49 | | [主頁](../../../) | [上一關](../level-18_flux-utils) | [下一關. 完成 Reducers:讓狀態的改變可預測化](../level-20_redux-reducers) | 50 | 51 | | :raising_hand: [我要提問](https://github.com/shiningjason1989/react-quick-tutorial/issues/new) | 52 | 53 | 54 | ![Analytics](https://shining-ga-beacon.appspot.com/UA-77436651-1/level-19_redux?pixel) 55 | -------------------------------------------------------------------------------- /level-20_redux-reducers/stores/TodoStore.js: -------------------------------------------------------------------------------- 1 | const { ReduceStore } = FluxUtils; 2 | 3 | const { 4 | ActionTypes, 5 | AppDispatcher 6 | } = window.App; 7 | 8 | const _createTodo = (todos, title) => { 9 | return [ 10 | ...todos, 11 | { 12 | id: todos[todos.length - 1].id + 1, 13 | title, 14 | completed: false 15 | } 16 | ]; 17 | }; 18 | 19 | const _updateTodo = (todos, id, title) => { 20 | const idx = todos.findIndex((todo) => todo.id === id); 21 | if (idx === -1) return todos; 22 | 23 | const newTodos = [ ...todos ]; 24 | newTodos[idx] = { 25 | ...todos[idx], 26 | title 27 | }; 28 | return newTodos; 29 | }; 30 | 31 | const _toggleTodo = (todos, id, completed) => { 32 | const idx = todos.findIndex((todo) => todo.id === id); 33 | if (idx === -1) return todos; 34 | 35 | const newTodos = [ ...todos ]; 36 | newTodos[idx] = { 37 | ...todos[idx], 38 | completed 39 | }; 40 | return newTodos; 41 | }; 42 | 43 | const _deleteTodo = (todos, id) => { 44 | const idx = todos.findIndex((todo) => todo.id === id); 45 | if (idx === -1) return todos; 46 | 47 | const newTodos = [ ...todos ]; 48 | newTodos.splice(idx, 1); 49 | return newTodos; 50 | }; 51 | 52 | class TodoStore extends ReduceStore { 53 | getInitialState() { 54 | return []; 55 | } 56 | 57 | reduce(state, action) { 58 | switch (action.type) { 59 | case ActionTypes.LOAD_TODOS_SUCCESS: 60 | return action.todos; 61 | case ActionTypes.CREATE_TODO: 62 | return _createTodo(state, action.title); 63 | case ActionTypes.UPDATE_TODO: 64 | return _updateTodo(state, action.id, action.title); 65 | case ActionTypes.TOGGLE_TODO: 66 | return _toggleTodo(state, action.id, action.completed); 67 | case ActionTypes.DELETE_TODO: 68 | return _deleteTodo(state, action.id); 69 | default: 70 | return state; 71 | } 72 | } 73 | } 74 | 75 | window.App.TodoStore = new TodoStore(AppDispatcher); 76 | -------------------------------------------------------------------------------- /level-21_redux-store/stores/TodoStore.js: -------------------------------------------------------------------------------- 1 | const { ReduceStore } = FluxUtils; 2 | 3 | const { 4 | ActionTypes, 5 | AppDispatcher 6 | } = window.App; 7 | 8 | const _createTodo = (todos, title) => { 9 | return [ 10 | ...todos, 11 | { 12 | id: todos[todos.length - 1].id + 1, 13 | title, 14 | completed: false 15 | } 16 | ]; 17 | }; 18 | 19 | const _updateTodo = (todos, id, title) => { 20 | const idx = todos.findIndex((todo) => todo.id === id); 21 | if (idx === -1) return todos; 22 | 23 | const newTodos = [ ...todos ]; 24 | newTodos[idx] = { 25 | ...todos[idx], 26 | title 27 | }; 28 | return newTodos; 29 | }; 30 | 31 | const _toggleTodo = (todos, id, completed) => { 32 | const idx = todos.findIndex((todo) => todo.id === id); 33 | if (idx === -1) return todos; 34 | 35 | const newTodos = [ ...todos ]; 36 | newTodos[idx] = { 37 | ...todos[idx], 38 | completed 39 | }; 40 | return newTodos; 41 | }; 42 | 43 | const _deleteTodo = (todos, id) => { 44 | const idx = todos.findIndex((todo) => todo.id === id); 45 | if (idx === -1) return todos; 46 | 47 | const newTodos = [ ...todos ]; 48 | newTodos.splice(idx, 1); 49 | return newTodos; 50 | }; 51 | 52 | class TodoStore extends ReduceStore { 53 | getInitialState() { 54 | return []; 55 | } 56 | 57 | reduce(state, action) { 58 | switch (action.type) { 59 | case ActionTypes.LOAD_TODOS_SUCCESS: 60 | return action.todos; 61 | case ActionTypes.CREATE_TODO: 62 | return _createTodo(state, action.title); 63 | case ActionTypes.UPDATE_TODO: 64 | return _updateTodo(state, action.id, action.title); 65 | case ActionTypes.TOGGLE_TODO: 66 | return _toggleTodo(state, action.id, action.completed); 67 | case ActionTypes.DELETE_TODO: 68 | return _deleteTodo(state, action.id); 69 | default: 70 | return state; 71 | } 72 | } 73 | } 74 | 75 | window.App.TodoStore = new TodoStore(AppDispatcher); 76 | -------------------------------------------------------------------------------- /level-22_redux-actions/stores/TodoStore.js: -------------------------------------------------------------------------------- 1 | const { ReduceStore } = FluxUtils; 2 | 3 | const { 4 | ActionTypes, 5 | AppDispatcher 6 | } = window.App; 7 | 8 | const _createTodo = (todos, title) => { 9 | return [ 10 | ...todos, 11 | { 12 | id: todos[todos.length - 1].id + 1, 13 | title, 14 | completed: false 15 | } 16 | ]; 17 | }; 18 | 19 | const _updateTodo = (todos, id, title) => { 20 | const idx = todos.findIndex((todo) => todo.id === id); 21 | if (idx === -1) return todos; 22 | 23 | const newTodos = [ ...todos ]; 24 | newTodos[idx] = { 25 | ...todos[idx], 26 | title 27 | }; 28 | return newTodos; 29 | }; 30 | 31 | const _toggleTodo = (todos, id, completed) => { 32 | const idx = todos.findIndex((todo) => todo.id === id); 33 | if (idx === -1) return todos; 34 | 35 | const newTodos = [ ...todos ]; 36 | newTodos[idx] = { 37 | ...todos[idx], 38 | completed 39 | }; 40 | return newTodos; 41 | }; 42 | 43 | const _deleteTodo = (todos, id) => { 44 | const idx = todos.findIndex((todo) => todo.id === id); 45 | if (idx === -1) return todos; 46 | 47 | const newTodos = [ ...todos ]; 48 | newTodos.splice(idx, 1); 49 | return newTodos; 50 | }; 51 | 52 | class TodoStore extends ReduceStore { 53 | getInitialState() { 54 | return []; 55 | } 56 | 57 | reduce(state, action) { 58 | switch (action.type) { 59 | case ActionTypes.LOAD_TODOS_SUCCESS: 60 | return action.todos; 61 | case ActionTypes.CREATE_TODO: 62 | return _createTodo(state, action.title); 63 | case ActionTypes.UPDATE_TODO: 64 | return _updateTodo(state, action.id, action.title); 65 | case ActionTypes.TOGGLE_TODO: 66 | return _toggleTodo(state, action.id, action.completed); 67 | case ActionTypes.DELETE_TODO: 68 | return _deleteTodo(state, action.id); 69 | default: 70 | return state; 71 | } 72 | } 73 | } 74 | 75 | window.App.TodoStore = new TodoStore(AppDispatcher); 76 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/stores/TodoStore.js: -------------------------------------------------------------------------------- 1 | const { ReduceStore } = FluxUtils; 2 | 3 | const { 4 | ActionTypes, 5 | AppDispatcher 6 | } = window.App; 7 | 8 | const _createTodo = (todos, title) => { 9 | return [ 10 | ...todos, 11 | { 12 | id: todos[todos.length - 1].id + 1, 13 | title, 14 | completed: false 15 | } 16 | ]; 17 | }; 18 | 19 | const _updateTodo = (todos, id, title) => { 20 | const idx = todos.findIndex((todo) => todo.id === id); 21 | if (idx === -1) return todos; 22 | 23 | const newTodos = [ ...todos ]; 24 | newTodos[idx] = { 25 | ...todos[idx], 26 | title 27 | }; 28 | return newTodos; 29 | }; 30 | 31 | const _toggleTodo = (todos, id, completed) => { 32 | const idx = todos.findIndex((todo) => todo.id === id); 33 | if (idx === -1) return todos; 34 | 35 | const newTodos = [ ...todos ]; 36 | newTodos[idx] = { 37 | ...todos[idx], 38 | completed 39 | }; 40 | return newTodos; 41 | }; 42 | 43 | const _deleteTodo = (todos, id) => { 44 | const idx = todos.findIndex((todo) => todo.id === id); 45 | if (idx === -1) return todos; 46 | 47 | const newTodos = [ ...todos ]; 48 | newTodos.splice(idx, 1); 49 | return newTodos; 50 | }; 51 | 52 | class TodoStore extends ReduceStore { 53 | getInitialState() { 54 | return []; 55 | } 56 | 57 | reduce(state, action) { 58 | switch (action.type) { 59 | case ActionTypes.LOAD_TODOS_SUCCESS: 60 | return action.todos; 61 | case ActionTypes.CREATE_TODO: 62 | return _createTodo(state, action.title); 63 | case ActionTypes.UPDATE_TODO: 64 | return _updateTodo(state, action.id, action.title); 65 | case ActionTypes.TOGGLE_TODO: 66 | return _toggleTodo(state, action.id, action.completed); 67 | case ActionTypes.DELETE_TODO: 68 | return _deleteTodo(state, action.id); 69 | default: 70 | return state; 71 | } 72 | } 73 | } 74 | 75 | window.App.TodoStore = new TodoStore(AppDispatcher); 76 | -------------------------------------------------------------------------------- /level-21_redux-store/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /level-22_redux-actions/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /level-23_redux-middlewares/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /level-15_flux-stores/stores/TodoStore.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | const CHANGE_EVENT = 'CHANGE'; 7 | 8 | const _emitter = new EventEmitter(); 9 | 10 | let _todos = []; 11 | 12 | const _createTodo = (todos, title) => { 13 | todos.push({ 14 | id: todos[todos.length - 1].id + 1, 15 | title, 16 | completed: false 17 | }); 18 | return todos; 19 | }; 20 | 21 | const _updateTodo = (todos, id, title) => { 22 | const target = todos.find((todo) => todo.id === id); 23 | if (target) target.title = title; 24 | return todos; 25 | }; 26 | 27 | const _toggleTodo = (todos, id, completed) => { 28 | const target = todos.find((todo) => todo.id === id); 29 | if (target) target.completed = completed; 30 | return todos; 31 | }; 32 | 33 | const _deleteTodo = (todos, id) => { 34 | const idx = todos.findIndex((todo) => todo.id === id); 35 | if (idx !== -1) todos.splice(idx, 1); 36 | return todos; 37 | }; 38 | 39 | window.App.TodoStore = { 40 | getAll() { 41 | return _todos; 42 | }, 43 | addChangeListener(callback) { 44 | _emitter.on(CHANGE_EVENT, callback); 45 | return () => _emitter.removeListener(CHANGE_EVENT, callback); 46 | }, 47 | dispatchToken: AppDispatcher.register((action) => { 48 | switch (action.type) { 49 | case ActionTypes.LOAD_TODOS_SUCCESS: 50 | _todos = action.todos; 51 | _emitter.emit(CHANGE_EVENT); 52 | break; 53 | case ActionTypes.CREATE_TODO: 54 | _todos = _createTodo(_todos, action.title); 55 | _emitter.emit(CHANGE_EVENT); 56 | break; 57 | case ActionTypes.UPDATE_TODO: 58 | _todos = _updateTodo(_todos, action.id, action.title); 59 | _emitter.emit(CHANGE_EVENT); 60 | break; 61 | case ActionTypes.TOGGLE_TODO: 62 | _todos = _toggleTodo(_todos, action.id, action.completed); 63 | _emitter.emit(CHANGE_EVENT); 64 | break; 65 | case ActionTypes.DELETE_TODO: 66 | _todos = _deleteTodo(_todos, action.id); 67 | _emitter.emit(CHANGE_EVENT); 68 | break; 69 | } 70 | }) 71 | }; 72 | -------------------------------------------------------------------------------- /level-16_flux-controller-view/stores/TodoStore.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | const CHANGE_EVENT = 'CHANGE'; 7 | 8 | const _emitter = new EventEmitter(); 9 | 10 | let _todos = []; 11 | 12 | const _createTodo = (todos, title) => { 13 | todos.push({ 14 | id: todos[todos.length - 1].id + 1, 15 | title, 16 | completed: false 17 | }); 18 | return todos; 19 | }; 20 | 21 | const _updateTodo = (todos, id, title) => { 22 | const target = todos.find((todo) => todo.id === id); 23 | if (target) target.title = title; 24 | return todos; 25 | }; 26 | 27 | const _toggleTodo = (todos, id, completed) => { 28 | const target = todos.find((todo) => todo.id === id); 29 | if (target) target.completed = completed; 30 | return todos; 31 | }; 32 | 33 | const _deleteTodo = (todos, id) => { 34 | const idx = todos.findIndex((todo) => todo.id === id); 35 | if (idx !== -1) todos.splice(idx, 1); 36 | return todos; 37 | }; 38 | 39 | window.App.TodoStore = { 40 | getAll() { 41 | return _todos; 42 | }, 43 | addChangeListener(callback) { 44 | _emitter.on(CHANGE_EVENT, callback); 45 | return () => _emitter.removeListener(CHANGE_EVENT, callback); 46 | }, 47 | dispatchToken: AppDispatcher.register((action) => { 48 | switch (action.type) { 49 | case ActionTypes.LOAD_TODOS_SUCCESS: 50 | _todos = action.todos; 51 | _emitter.emit(CHANGE_EVENT); 52 | break; 53 | case ActionTypes.CREATE_TODO: 54 | _todos = _createTodo(_todos, action.title); 55 | _emitter.emit(CHANGE_EVENT); 56 | break; 57 | case ActionTypes.UPDATE_TODO: 58 | _todos = _updateTodo(_todos, action.id, action.title); 59 | _emitter.emit(CHANGE_EVENT); 60 | break; 61 | case ActionTypes.TOGGLE_TODO: 62 | _todos = _toggleTodo(_todos, action.id, action.completed); 63 | _emitter.emit(CHANGE_EVENT); 64 | break; 65 | case ActionTypes.DELETE_TODO: 66 | _todos = _deleteTodo(_todos, action.id); 67 | _emitter.emit(CHANGE_EVENT); 68 | break; 69 | } 70 | }) 71 | }; 72 | -------------------------------------------------------------------------------- /level-17_container-pattern/stores/TodoStore.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionTypes, 3 | AppDispatcher 4 | } = window.App; 5 | 6 | const CHANGE_EVENT = 'CHANGE'; 7 | 8 | const _emitter = new EventEmitter(); 9 | 10 | let _todos = []; 11 | 12 | const _createTodo = (todos, title) => { 13 | todos.push({ 14 | id: todos[todos.length - 1].id + 1, 15 | title, 16 | completed: false 17 | }); 18 | return todos; 19 | }; 20 | 21 | const _updateTodo = (todos, id, title) => { 22 | const target = todos.find((todo) => todo.id === id); 23 | if (target) target.title = title; 24 | return todos; 25 | }; 26 | 27 | const _toggleTodo = (todos, id, completed) => { 28 | const target = todos.find((todo) => todo.id === id); 29 | if (target) target.completed = completed; 30 | return todos; 31 | }; 32 | 33 | const _deleteTodo = (todos, id) => { 34 | const idx = todos.findIndex((todo) => todo.id === id); 35 | if (idx !== -1) todos.splice(idx, 1); 36 | return todos; 37 | }; 38 | 39 | window.App.TodoStore = { 40 | getAll() { 41 | return _todos; 42 | }, 43 | addChangeListener(callback) { 44 | _emitter.on(CHANGE_EVENT, callback); 45 | return () => _emitter.removeListener(CHANGE_EVENT, callback); 46 | }, 47 | dispatchToken: AppDispatcher.register((action) => { 48 | switch (action.type) { 49 | case ActionTypes.LOAD_TODOS_SUCCESS: 50 | _todos = action.todos; 51 | _emitter.emit(CHANGE_EVENT); 52 | break; 53 | case ActionTypes.CREATE_TODO: 54 | _todos = _createTodo(_todos, action.title); 55 | _emitter.emit(CHANGE_EVENT); 56 | break; 57 | case ActionTypes.UPDATE_TODO: 58 | _todos = _updateTodo(_todos, action.id, action.title); 59 | _emitter.emit(CHANGE_EVENT); 60 | break; 61 | case ActionTypes.TOGGLE_TODO: 62 | _todos = _toggleTodo(_todos, action.id, action.completed); 63 | _emitter.emit(CHANGE_EVENT); 64 | break; 65 | case ActionTypes.DELETE_TODO: 66 | _todos = _deleteTodo(_todos, action.id); 67 | _emitter.emit(CHANGE_EVENT); 68 | break; 69 | } 70 | }) 71 | }; 72 | -------------------------------------------------------------------------------- /level-20_redux-reducers/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TodoApp 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 38 | 39 | -------------------------------------------------------------------------------- /level-11_component-lifecycle/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | InputField, 3 | TodoHeader, 4 | TodoList 5 | } = window.App; 6 | 7 | const _createTodo = (todos, title) => { 8 | todos.push({ 9 | id: todos[todos.length - 1].id + 1, 10 | title, 11 | completed: false 12 | }); 13 | return todos; 14 | }; 15 | 16 | const _updateTodo = (todos, id, title) => { 17 | const target = todos.find((todo) => todo.id === id); 18 | if (target) target.title = title; 19 | return todos; 20 | }; 21 | 22 | const _toggleTodo = (todos, id, completed) => { 23 | const target = todos.find((todo) => todo.id === id); 24 | if (target) target.completed = completed; 25 | return todos; 26 | }; 27 | 28 | const _deleteTodo = (todos, id) => { 29 | const idx = todos.findIndex((todo) => todo.id === id); 30 | if (idx !== -1) todos.splice(idx, 1); 31 | return todos; 32 | }; 33 | 34 | class TodoApp extends React.Component { 35 | constructor(props, context) { 36 | super(props, context); 37 | this.state = { 38 | todos: [] 39 | }; 40 | } 41 | 42 | componentDidMount() { 43 | fetch('./todos.json') 44 | .then((response) => response.json()) 45 | .then((todos) => this.setState({ todos })); 46 | } 47 | 48 | updateTodosBy(updateFn) { 49 | return (...args) => { 50 | this.setState({ 51 | todos: updateFn(this.state.todos, ...args) 52 | }); 53 | }; 54 | } 55 | 56 | render() { 57 | const { todos } = this.state; 58 | return ( 59 |
    60 | !todo.completed).length} 64 | /> 65 | 69 | 75 |
    76 | ); 77 | } 78 | } 79 | 80 | window.App.TodoApp = TodoApp; 81 | -------------------------------------------------------------------------------- /level-14_flux-actions/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | InputField, 3 | TodoHeader, 4 | TodoList 5 | } = window.App; 6 | 7 | const _createTodo = (todos, title) => { 8 | todos.push({ 9 | id: todos[todos.length - 1].id + 1, 10 | title, 11 | completed: false 12 | }); 13 | return todos; 14 | }; 15 | 16 | const _updateTodo = (todos, id, title) => { 17 | const target = todos.find((todo) => todo.id === id); 18 | if (target) target.title = title; 19 | return todos; 20 | }; 21 | 22 | const _toggleTodo = (todos, id, completed) => { 23 | const target = todos.find((todo) => todo.id === id); 24 | if (target) target.completed = completed; 25 | return todos; 26 | }; 27 | 28 | const _deleteTodo = (todos, id) => { 29 | const idx = todos.findIndex((todo) => todo.id === id); 30 | if (idx !== -1) todos.splice(idx, 1); 31 | return todos; 32 | }; 33 | 34 | class TodoApp extends React.Component { 35 | constructor(props, context) { 36 | super(props, context); 37 | this.state = { 38 | todos: [] 39 | }; 40 | } 41 | 42 | componentDidMount() { 43 | fetch('./todos.json') 44 | .then((response) => response.json()) 45 | .then((todos) => this.setState({ todos })); 46 | } 47 | 48 | updateTodosBy(updateFn) { 49 | return (...args) => { 50 | this.setState({ 51 | todos: updateFn(this.state.todos, ...args) 52 | }); 53 | }; 54 | } 55 | 56 | render() { 57 | const { todos } = this.state; 58 | return ( 59 |
    60 | !todo.completed).length} 64 | /> 65 | 69 | 75 |
    76 | ); 77 | } 78 | } 79 | 80 | window.App.TodoApp = TodoApp; 81 | -------------------------------------------------------------------------------- /level-15_flux-stores/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | InputField, 3 | TodoHeader, 4 | TodoList 5 | } = window.App; 6 | 7 | const _createTodo = (todos, title) => { 8 | todos.push({ 9 | id: todos[todos.length - 1].id + 1, 10 | title, 11 | completed: false 12 | }); 13 | return todos; 14 | }; 15 | 16 | const _updateTodo = (todos, id, title) => { 17 | const target = todos.find((todo) => todo.id === id); 18 | if (target) target.title = title; 19 | return todos; 20 | }; 21 | 22 | const _toggleTodo = (todos, id, completed) => { 23 | const target = todos.find((todo) => todo.id === id); 24 | if (target) target.completed = completed; 25 | return todos; 26 | }; 27 | 28 | const _deleteTodo = (todos, id) => { 29 | const idx = todos.findIndex((todo) => todo.id === id); 30 | if (idx !== -1) todos.splice(idx, 1); 31 | return todos; 32 | }; 33 | 34 | class TodoApp extends React.Component { 35 | constructor(props, context) { 36 | super(props, context); 37 | this.state = { 38 | todos: [] 39 | }; 40 | } 41 | 42 | componentDidMount() { 43 | fetch('./todos.json') 44 | .then((response) => response.json()) 45 | .then((todos) => this.setState({ todos })); 46 | } 47 | 48 | updateTodosBy(updateFn) { 49 | return (...args) => { 50 | this.setState({ 51 | todos: updateFn(this.state.todos, ...args) 52 | }); 53 | }; 54 | } 55 | 56 | render() { 57 | const { todos } = this.state; 58 | return ( 59 |
    60 | !todo.completed).length} 64 | /> 65 | 69 | 75 |
    76 | ); 77 | } 78 | } 79 | 80 | window.App.TodoApp = TodoApp; 81 | -------------------------------------------------------------------------------- /level-13_flux-dispatcher/components/TodoApp.js: -------------------------------------------------------------------------------- 1 | const { 2 | InputField, 3 | TodoHeader, 4 | TodoList 5 | } = window.App; 6 | 7 | const _createTodo = (todos, title) => { 8 | todos.push({ 9 | id: todos[todos.length - 1].id + 1, 10 | title, 11 | completed: false 12 | }); 13 | return todos; 14 | }; 15 | 16 | const _updateTodo = (todos, id, title) => { 17 | const target = todos.find((todo) => todo.id === id); 18 | if (target) target.title = title; 19 | return todos; 20 | }; 21 | 22 | const _toggleTodo = (todos, id, completed) => { 23 | const target = todos.find((todo) => todo.id === id); 24 | if (target) target.completed = completed; 25 | return todos; 26 | }; 27 | 28 | const _deleteTodo = (todos, id) => { 29 | const idx = todos.findIndex((todo) => todo.id === id); 30 | if (idx !== -1) todos.splice(idx, 1); 31 | return todos; 32 | }; 33 | 34 | class TodoApp extends React.Component { 35 | constructor(props, context) { 36 | super(props, context); 37 | this.state = { 38 | todos: [] 39 | }; 40 | } 41 | 42 | componentDidMount() { 43 | fetch('./todos.json') 44 | .then((response) => response.json()) 45 | .then((todos) => this.setState({ todos })); 46 | } 47 | 48 | updateTodosBy(updateFn) { 49 | return (...args) => { 50 | this.setState({ 51 | todos: updateFn(this.state.todos, ...args) 52 | }); 53 | }; 54 | } 55 | 56 | render() { 57 | const { todos } = this.state; 58 | return ( 59 |
    60 | !todo.completed).length} 64 | /> 65 | 69 | 75 |
    76 | ); 77 | } 78 | } 79 | 80 | window.App.TodoApp = TodoApp; 81 | --------------------------------------------------------------------------------