@@ -52,11 +60,27 @@ class TodoList extends Component {
{todos}
- {todo}
New Todo
)
}
}
-export default TodoList;
\ No newline at end of file
+const mapDispatchToProps = dispatch => {
+ return {
+ onDeleteTodo: (id) => {
+ dispatch(actionCreators.deleteTodo(id))
+ },
+ onToggleTodo: (id) => {
+ dispatch(actionCreators.toggleTodo(id))
+ },
+ onGetAll: () => dispatch(actionCreators.getTodos())
+ }
+}
+const mapStateToProps = state => {
+ return {
+ storedTodos: state.td.todos,
+ selectedTodo: state.td.selectedTodo,
+ };
+};
+export default connect(mapStateToProps, mapDispatchToProps)(withRouter(TodoList));
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 87d1be5..ff0cdd6 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,8 +3,23 @@ import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
+import {Provider} from 'react-redux';
+import {createStore, combineReducers} from 'redux';
+import { applyMiddleware } from 'redux';
+import thunk from 'redux-thunk';
+import {connectRouter, routerMiddleware } from 'connected-react-router';
+import {createBrowserHistory} from 'history';
-ReactDOM.render(
, document.getElementById('root'));
+import todoReducer from './store/reducers/todo';
+
+
+const history = createBrowserHistory();
+
+const rootReducer = combineReducers({
+ td: todoReducer, router: connectRouter(history)
+})
+const store = createStore(rootReducer, applyMiddleware(thunk, routerMiddleware(history)));
+ReactDOM.render(
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
diff --git a/src/redux-basics.js b/src/redux-basics.js
new file mode 100644
index 0000000..0566f79
--- /dev/null
+++ b/src/redux-basics.js
@@ -0,0 +1,27 @@
+const redux = require('redux');
+const createStore = redux.createStore;
+const initialState = {number: 0};
+
+
+
+const reducer = (state = initialState, action) => {
+ if (action.type == 'ADD'){
+ return ({...state, number: state.number+1});
+ }
+ else if (action.type == 'ADD_VALUE'){
+ return ({
+ ...state, number: state.number+action.value
+ })
+ }
+ return state;
+}
+const store = createStore(reducer);
+
+store.subscribe( () =>{
+ console.log('[Subscription]', store.getState());
+});
+store.dispatch({type: 'ADD'});
+store.dispatch({type: 'ADD_VALUE', value: 5});
+console.log(store.getState());
+
+
diff --git a/src/store/actions/actionTypes.js b/src/store/actions/actionTypes.js
new file mode 100644
index 0000000..b954b80
--- /dev/null
+++ b/src/store/actions/actionTypes.js
@@ -0,0 +1,5 @@
+export const GET_ALL = 'GET_ALL';
+export const GET_TODO = 'GET_TODO';
+export const TOGGLE_DONE = 'TOGGLE_DONE';
+export const DELETE_TODO = 'DELETE_TODO';
+export const ADD_TODO = 'ADD_TODO';
\ No newline at end of file
diff --git a/src/store/actions/index.js b/src/store/actions/index.js
new file mode 100644
index 0000000..a721c4b
--- /dev/null
+++ b/src/store/actions/index.js
@@ -0,0 +1,7 @@
+export {
+ getTodos,
+ postTodo,
+ deleteTodo,
+ toggleTodo,
+ getTodo
+} from './todo';
\ No newline at end of file
diff --git a/src/store/actions/todo.js b/src/store/actions/todo.js
new file mode 100644
index 0000000..76c4727
--- /dev/null
+++ b/src/store/actions/todo.js
@@ -0,0 +1,79 @@
+import * as actionTypes from './actionTypes';
+import axios from 'axios';
+import {push} from 'connected-react-router';
+
+export const getTodos_ = (todos) => {
+ return { type: actionTypes.GET_ALL, todos: todos};
+};
+
+export const getTodos = () => {
+ return dispatch => {
+ return axios.get('/api/todo')
+ .then(res => dispatch(getTodos_(res.data)));
+ }
+}
+
+export const postTodo_ = (td) => {
+ return {
+ type: actionTypes.ADD_TODO,
+ id: td.id,
+ title: td.title,
+ content: td.content
+ };
+};
+
+export const postTodo = (td) => {
+ return (dispatch) => {
+ return axios.post('/api/todo/', td)
+ .then(res => dispatch(postTodo_(res.data)))
+ .then(() => dispatch(push('/todos/')))
+ }
+}
+
+export const deleteTodo_ = (id) => {
+ return {
+ type: actionTypes.DELETE_TODO,
+ targetID: id
+ };
+};
+
+export const deleteTodo = (id) => {
+ return (dispatch) => {
+ return axios.delete('/api/todo/'+id)
+ .then(res => {
+ dispatch(deleteTodo_(id));
+ })
+ }
+}
+
+export const toggleTodo_ = (id) => {
+ return {
+ type: actionTypes.TOGGLE_DONE,
+ targetID: id
+ };
+};
+
+export const toggleTodo = (id) =>{
+ return (dispatch) => {
+ return axios.put('/api/todo/'+id)
+ .then(res => {
+ dispatch(toggleTodo_(id));
+ })
+ }
+}
+
+export const getTodo_ = (todo) => {
+ return {
+ type: actionTypes.GET_TODO,
+ target: todo
+ };
+};
+
+export const getTodo = (id) => {
+ return (dispatch) => {
+ return axios.get('/api/todo/'+id)
+ .then(res =>{
+ dispatch(getTodo_(res.data));
+ })
+ }
+}
\ No newline at end of file
diff --git a/src/store/reducers/todo.js b/src/store/reducers/todo.js
new file mode 100644
index 0000000..8133c10
--- /dev/null
+++ b/src/store/reducers/todo.js
@@ -0,0 +1,52 @@
+import * as actionTypes from '../actions/actionTypes';
+
+const initialState = {
+ todos: [
+ { id: 1, title: 'SWPP', content: 'take swpp class', done: true },
+ { id: 2, title: 'Movie', content: 'watch movie', done: false },
+ { id: 3, title: 'Dinner', content: 'eat dinner', done: false }
+ ],
+ selectedTodo: null
+};
+
+const reducer = (state = initialState, action) =>{
+ switch(action.type){
+ case actionTypes.ADD_TODO:
+ const newTodo = {
+ id: action.id,
+ title: action.title,
+ content: action.content,
+ done: action.done
+ }
+ return {...state, todos: [...state.todos, newTodo]};
+ case actionTypes.DELETE_TODO:
+ const deleted = state.todos.filter( (todo) => {
+ return todo.id!==action.targetID;
+ });
+ return {...state, todos:deleted};
+ case actionTypes.TOGGLE_DONE:
+ const modified = state.todos.map( (todo) => {
+ if(todo.id === action.targetID){
+ return {...todo, done: !todo.done};
+ }
+ else{
+ return {...todo};
+ }
+ });
+ return {...state, todos: modified};
+ case actionTypes.GET_TODO:
+ return {
+ ...state,
+ selectedTodo: action.target
+ }
+ case actionTypes.GET_ALL:
+ return {
+ ...state, todos: action.todos
+ };
+ default:
+ break;
+ }
+ return state;
+}
+
+export default reducer;
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 54cccc1..584c215 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -770,6 +770,13 @@
dependencies:
regenerator-runtime "^0.13.2"
+"@babel/runtime@^7.5.5":
+ version "7.11.2"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
+ integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
@@ -1748,6 +1755,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
+axios@^0.20.0:
+ version "0.20.0"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd"
+ integrity sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==
+ dependencies:
+ follow-redirects "^1.10.0"
+
axobject-query@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9"
@@ -2551,6 +2565,13 @@ connect-history-api-fallback@^1.3.0:
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
+connected-react-router@^6.8.0:
+ version "6.8.0"
+ resolved "https://registry.yarnpkg.com/connected-react-router/-/connected-react-router-6.8.0.tgz#ddc687b31d498322445d235d660798489fa56cae"
+ integrity sha512-E64/6krdJM3Ag3MMmh2nKPtMbH15s3JQDuaYJvOVXzu6MbHbDyIvuwLOyhQIuP4Om9zqEfZYiVyflROibSsONg==
+ dependencies:
+ prop-types "^15.7.2"
+
console-browserify@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
@@ -4005,6 +4026,11 @@ follow-redirects@^1.0.0:
dependencies:
debug "^3.2.6"
+follow-redirects@^1.10.0:
+ version "1.13.0"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
+ integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
+
for-in@^0.1.3:
version "0.1.8"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
@@ -4429,6 +4455,13 @@ hoist-non-react-statics@^3.1.0:
dependencies:
react-is "^16.7.0"
+hoist-non-react-statics@^3.3.0:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
+ integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
+ dependencies:
+ react-is "^16.7.0"
+
hosted-git-info@^2.1.4:
version "2.8.4"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546"
@@ -7944,6 +7977,22 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb"
integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==
+react-is@^16.9.0:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+react-redux@^7.2.1:
+ version "7.2.1"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.1.tgz#8dedf784901014db2feca1ab633864dee68ad985"
+ integrity sha512-T+VfD/bvgGTUA74iW9d2i5THrDQWbweXP0AVNI8tNd1Rk5ch1rnMiJkDD67ejw7YBKM4+REvcvqRuWJb7BLuEg==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ hoist-non-react-statics "^3.3.0"
+ loose-envify "^1.4.0"
+ prop-types "^15.7.2"
+ react-is "^16.9.0"
+
react-router-dom@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be"
@@ -8122,6 +8171,19 @@ recursive-readdir@2.2.2:
dependencies:
minimatch "3.0.4"
+redux-thunk@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
+ integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
+
+redux@^4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
+ integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
+ dependencies:
+ loose-envify "^1.4.0"
+ symbol-observable "^1.2.0"
+
regenerate-unicode-properties@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
@@ -8144,6 +8206,11 @@ regenerator-runtime@^0.11.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+regenerator-runtime@^0.13.4:
+ version "0.13.7"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
+ integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
+
regenerator-transform@^0.14.0:
version "0.14.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb"
@@ -9115,6 +9182,11 @@ svgo@^1.0.0, svgo@^1.2.2:
unquote "~1.1.1"
util.promisify "~1.0.0"
+symbol-observable@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+ integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
symbol-tree@^3.2.2:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"