Skip to content

Commit e58c473

Browse files
committed
Add a simple Todo as an example
1 parent 9deb948 commit e58c473

File tree

11 files changed

+158
-21
lines changed

11 files changed

+158
-21
lines changed

common/js/actions/.gitkeep

Whitespace-only changes.

common/js/actions/todos.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { ADD_TODO, REMOVE_TODO, TOGGLE_TODO } from 'constants';
2+
import generateActionCreator from 'lib/generateActionCreator';
3+
4+
export const addTodo = generateActionCreator(ADD_TODO, 'text');
5+
export const removeTodo = generateActionCreator(REMOVE_TODO, 'id');
6+
export const toggleTodo = generateActionCreator(TOGGLE_TODO, 'id');

common/js/components/Header.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ export default class Header extends Component {
88
<header>
99
<ul className={css.headerLinks}>
1010
<li><Link to="/">Home</Link></li>
11-
<li><Link to="/example">Example</Link></li>
12-
<li><Link to="/example/2">Example 2</Link></li>
11+
<li><Link to="/todos">Todos</Link></li>
1312
</ul>
1413
</header>
1514
);

common/js/constants/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const ADD_TODO = 'ADD_TODO';
2+
export const REMOVE_TODO = 'REMOVE_TODO';
3+
export const TOGGLE_TODO = 'TOGGLE_TODO';
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import React, { Component } from 'react';
2+
import PropTypes from 'prop-types';
3+
import { connect } from 'react-redux';
4+
import { addTodo, toggleTodo, removeTodo } from 'actions/todos';
5+
import classnames from 'classnames';
6+
import css from './index.scss';
7+
8+
const cx = classnames.bind(css);
9+
10+
class TodosContainer extends Component {
11+
12+
static propTypes = {
13+
todos: PropTypes.array.isRequired,
14+
dispatch: PropTypes.func.isRequired
15+
}
16+
17+
submitTodo = (ev) => {
18+
const { dispatch } = this.props;
19+
const { todoText } = this.refs;
20+
21+
ev.preventDefault();
22+
dispatch(addTodo(todoText.value));
23+
todoText.value = '';
24+
}
25+
26+
checkTodo = (id) => {
27+
const { dispatch } = this.props;
28+
29+
dispatch(toggleTodo(id));
30+
}
31+
32+
removeTodo = (id) => {
33+
const { dispatch } = this.props;
34+
35+
dispatch(removeTodo(id));
36+
}
37+
38+
render() {
39+
const { todos } = this.props;
40+
41+
return (
42+
<div>
43+
<h1>To-Dos</h1>
44+
<div className={css.todos}>
45+
{todos.map((todo, idx) => {
46+
const { id, text, completed } = todo;
47+
48+
return (
49+
<li key={idx} className={css.todo}>
50+
<span className={css.completeInput}>
51+
<input type="checkbox" onChange={() => this.checkTodo(id)} />
52+
</span>
53+
<span className={cx(css.text, { [css.completed]: completed })}>{text}</span>
54+
<a onClick={() => this.removeTodo(id)} className={css.delete}>Remove</a>
55+
</li>
56+
);
57+
})}
58+
</div>
59+
<form className={css.todoForm} onSubmit={this.submitTodo}>
60+
<input ref="todoText" type="text" placeholder="Add a todo..." />
61+
<button type="submit">Add</button>
62+
</form>
63+
</div>
64+
);
65+
}
66+
}
67+
68+
const mapStateToProps = (state) => ({
69+
todos: state.todos
70+
});
71+
72+
export default connect(mapStateToProps)(TodosContainer);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
.todos {
2+
width: 300px;
3+
margin: 0;
4+
padding: 0;
5+
list-style: none;
6+
7+
.todo {
8+
display: flex;
9+
justify-content: space-between;
10+
padding: 10px 0;
11+
border-bottom: 1px solid #eee;
12+
}
13+
.todo .completeInput {
14+
flex: 0;
15+
width: 25px;
16+
}
17+
.todo .text {
18+
flex: 1;
19+
padding-left: 10px;
20+
21+
&.completed {
22+
text-decoration: line-through;
23+
color: #ccc;
24+
}
25+
}
26+
.todo .delete {
27+
flex: 0;
28+
cursor: pointer;
29+
color: blue;
30+
text-decoration: underline
31+
}
32+
}
33+
34+
.todoForm {
35+
width: 300px;
36+
margin: 10px 0 0;
37+
display: flex;
38+
39+
input, button {
40+
padding: 5px 3px;
41+
}
42+
43+
input { flex: 1; }
44+
}

common/js/lib/.gitkeep

Whitespace-only changes.

common/js/reducers/example.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

common/js/reducers/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { combineReducers } from 'redux';
22
import { routerReducer } from 'react-router-redux';
33

44
// Import your reducers here
5-
import example from './example';
5+
import todos from './todos';
66

77
const rootReducer = combineReducers({
88
routing: routerReducer,
9-
example
9+
todos
1010
});
1111

1212
export default rootReducer;

common/js/reducers/todos.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ADD_TODO, REMOVE_TODO, TOGGLE_TODO } from 'constants';
2+
3+
const defaultState = [];
4+
5+
const todos = (state = defaultState, action) => {
6+
switch (action.type) {
7+
case ADD_TODO:
8+
return [...state, { id: Date.now(), text: action.text, completed: false }];
9+
10+
case REMOVE_TODO:
11+
return state.filter(todo => todo.id !== action.id);
12+
13+
case TOGGLE_TODO:
14+
return state.map(todo => {
15+
if (todo.id === action.id) {
16+
return { ...todo, completed: !todo.completed };
17+
}
18+
return todo;
19+
});
20+
21+
default:
22+
return state;
23+
}
24+
};
25+
26+
export default todos;

0 commit comments

Comments
 (0)