diff --git a/redux-basic/redux-basics.js b/redux-basic/redux-basics.js
new file mode 100644
index 0000000..6f8a16f
--- /dev/null
+++ b/redux-basic/redux-basics.js
@@ -0,0 +1,25 @@
+const redux = require('redux'); // load module in Node.js
+const createStore = redux.createStore;
+const initialState = { number: 0 }; // default state
+
+// create identity reducer
+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' });
+console.log(store.getState());
+store.dispatch({ type: 'ADD_VALUE', value: 5 });
+console.log(store.getState());
\ No newline at end of file
diff --git a/src/components/Todo/Todo.js b/src/components/Todo/Todo.js
index 9c80927..a9625cf 100644
--- a/src/components/Todo/Todo.js
+++ b/src/components/Todo/Todo.js
@@ -7,11 +7,12 @@ const Todo = (props) => {
return (
+ className={`text ${props.done && 'done'}`} onClick={props.clickDetail}>
{props.title}
{props.done &&
✓
}
+
+
);
};
diff --git a/src/containers/TodoList/NewTodo/NewTodo.js b/src/containers/TodoList/NewTodo/NewTodo.js
index 59e7930..3da3299 100644
--- a/src/containers/TodoList/NewTodo/NewTodo.js
+++ b/src/containers/TodoList/NewTodo/NewTodo.js
@@ -1,5 +1,7 @@
import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import * as actionTypes from '../../../store/actions/actionTypes';
import { Redirect } from 'react-router-dom';
import './NewTodo.css';
@@ -14,6 +16,8 @@ class NewTodo extends Component {
postTodoHandler = () => {
const data =
{ title: this.state.title, content: this.state.content }
+ this.props.onStoreTodo(this.state.title, this.state.content);
+ this.setState({ submitted: true });
alert('submitted' + data.title);
// this.props.history.push('/todos');
this.props.history.goBack();
@@ -36,7 +40,7 @@ class NewTodo extends Component {
>
@@ -45,4 +49,10 @@ class NewTodo extends Component {
}
}
-export default NewTodo;
\ No newline at end of file
+const mapDispatchToProps = dispatch => {
+ return {
+ onStoreTodo: (title, content) =>
+ dispatch({ type: actionTypes.ADD_TODO, title: title, content: content })
+ };
+};
+export default connect(null, mapDispatchToProps)(NewTodo);
\ No newline at end of file
diff --git a/src/containers/TodoList/RealDetail/RealDetail.js b/src/containers/TodoList/RealDetail/RealDetail.js
index 89500a3..c6e7159 100644
--- a/src/containers/TodoList/RealDetail/RealDetail.js
+++ b/src/containers/TodoList/RealDetail/RealDetail.js
@@ -1,23 +1,34 @@
import React, { Component } from 'react';
-
+import { connect } from 'react-redux';
import './RealDetail.css';
+import * as actionTypes from '../../../store/actions/actionTypes';
class RealDetail extends Component {
+ componentDidMount() {
+ this.props.onGetTodo(parseInt(this.props.match.params.id));
+ }
render() {
+ let content = '', title = '';
+ if (this.props.selectedTodo) {
+ title = this.props.selectedTodo.title;
+ content = this.props.selectedTodo.content;
+ }
return (
- Name:
+ Name:
+ {this.props.selectedTodo.title}
- Content:
+ Content:
+ {this.props.selectedTodo.content}
@@ -25,4 +36,19 @@ class RealDetail extends Component {
}
};
-export default RealDetail;
\ No newline at end of file
+
+
+const mapStateToProps = state => {
+ return {
+ selectedTodo: state.td.selectedTodo,
+ };
+};
+
+const mapDispatchToProps = dispatch => {
+ return {
+ onGetTodo: id =>
+ dispatch({ type: actionTypes.GET_TODO, targetID: id }),
+ };
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(RealDetail);
\ No newline at end of file
diff --git a/src/containers/TodoList/TodoList.js b/src/containers/TodoList/TodoList.js
index 6cb72c1..af0322c 100644
--- a/src/containers/TodoList/TodoList.js
+++ b/src/containers/TodoList/TodoList.js
@@ -1,13 +1,21 @@
import React, { Component } from 'react';
-
+import axios from 'axios';
import Todo from '../../components/Todo/Todo';
import TodoDetail from '../../components/TodoDetail/TodoDetail';
+import { withRouter } from 'react-router';
+import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
+import * as actionTypes from '../../store/actions/actionTypes';
import './TodoList.css';
class TodoList extends Component {
+ componentDidMount() {
+ axios.get("/api/todo")
+ .then(result => console.log(result.data))
+ .catch(error => console.error(`Oops! Error occurred!! ${error}`))
+ }
state = {
todos: [
{ id: 1, title: 'SWPP', content: 'take swpp class', done: true },
@@ -18,21 +26,20 @@ class TodoList extends Component {
}
clickTodoHandler = (td) => {
- if (this.state.selectedTodo === td) {
- this.setState({ ...this.state, selectedTodo: null });
- } else {
- this.setState({ ...this.state, selectedTodo: td });
- }
- }
+ this.props.history.push('/todos/' + td.id); }
+
render() {
- const todos = this.state.todos.map(td => {
+ const todos = this.props.storedTodos.map(td => {
return (
this.clickTodoHandler(td)}
+ clickDetail={() => this.clickTodoHandler(td)}
+ clickDone={() => this.props.onToggleTodo(td.id)}
+ clickDelete={() => this.props.onDeleteTodo(td.id)}
/>
);
});
@@ -59,4 +66,18 @@ class TodoList extends Component {
}
}
-export default TodoList;
\ No newline at end of file
+const mapStateToProps = state => {
+ return {
+ storedTodos: state.td.todos
+ };
+};
+
+const mapDispatchToProps = dispatch => {
+ return {
+ onToggleTodo: (id) => dispatch({ type: actionTypes.TOGGLE_DONE, targetID: id }),
+ onDeleteTodo: (id) => dispatch({ type: actionTypes.DELETE_TODO, targetID: id }),
+ onGetAll: () => dispatch(actionCreators.getTodos()),
+ };
+}; // don’t forget import * as actionTypes from ‘../../store/actions/actionTypes’;
+
+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..05f475e 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,8 +3,22 @@ import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
+import thunk from 'redux-thunk';
+import { applyMiddleware } from 'redux';
-ReactDOM.render(, document.getElementById('root'));
+// At index.js of project root
+import { Provider } from 'react-redux';
+import { createStore, comebineReducers, applyMiddleware } from 'redux';
+import todoReducer from './store/reducers/todo';
+
+const rootReducer = combineReducers({
+ td: todoReducer,
+});
+const store = createStore(rootReducer, applyMiddleware());
+
+
+const store = createStore((state = {}, action) => state); // TODO
+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/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..3edbbea
--- /dev/null
+++ b/src/store/actions/index.js
@@ -0,0 +1 @@
+export { getTodos } 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..ad7e286
--- /dev/null
+++ b/src/store/actions/todo.js
@@ -0,0 +1,14 @@
+import * as actionTypes from './actionTypes';
+import axios from 'axios';
+
+export const getTodos_ = (todos) => {
+ return { type: actionTypes.GET_ALL, todos: todos };
+};
+
+export const getTodos = () => { // Why no argument? We’ll see later.
+ return dispatch => {
+ return axios
+ .get('/api/todo')
+ .then(res => dispatch(getTodos_(res.data)));
+ }
+}
diff --git a/src/store/reducers/todo.js b/src/store/reducers/todo.js
new file mode 100644
index 0000000..339be54
--- /dev/null
+++ b/src/store/reducers/todo.js
@@ -0,0 +1,45 @@
+import { ADD_TODO, DELETE_TODO, TOGGLE_DONE, GET_TODO } 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) {
+ // we will handle actions via switch statement
+ case ADD_TODO:
+ // as React, do not mutate state directly, make new object
+ const newTodo = {
+ id: state.todos.length + 1, // temporary
+ title: action.title, content: action.content, done:false
+ }
+ return {...state, todos: state.todos.concat(newTodo)};
+ default:
+ return state;
+ case DELETE_TODO:
+ const deleted = state.todos.filter((todo) => {
+ return todo.id !== action.targetID;
+ });
+ return { ...state, todos: deleted };
+ case 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 GET_TODO:
+ const target = {...state.todos[action.targetID - 1]}; // temporary
+ return { ...state, selectedTodo: target };
+
+ return state;
+ case GET_ALL:
+ return {...state, todos: action.todos };
+ }
+ }
+export default reducer;
\ No newline at end of file