app\containers\SampleFullstackApps\Todo\reducers\todoConstants.js
export const LOAD_TASK = 'LOAD_TASK';
export const LOAD_TASKS_FULFILLED = 'LOAD_TASKS_FULFILLED';
app\containers\SampleFullstackApps\Todo\reducers\todoActions.js
export const loadTasksFulfilled = tasks => ({
type: types.LOAD_TASKS_FULFILLED,
payload: { tasks }
});
app\containers\SampleFullstackApps\Todo\reducers\todoSagas.js
function* read() {
const channel = yield call(subscribe);
while (true) {
const action = yield take(channel);
yield put(action);
}
}
function* watchLoadTask() {
while (true) {
taskList.path = 'tasks/';
const job = yield fork(read);
yield take([LOAD_TASK]);
yield cancel(job);
}
}
import TodoModels from '../models';
import Init from '../models/init';
import {
loadTasksFulfilled,
} from './todoActions';
function subscribe() {
return eventChannel(emit => taskList.subscribe(emit));
}
const taskList = new TodoModels({
onLoad: loadTasksFulfilled,
}, Init);
app\containers\SampleFullstackApps\Todo\reducers\todoReducer.js
case LOAD_TASKS_FULFILLED:
return state.merge({
list: new List(payload.tasks.reverse()),
loading: false,
});
app/containers/SampleFullstackApps/Todo/index.js
import { getVisibleTasks } from './reducers/selectors';
const { task } = this.props;
return (
<TaskList
loading={loading}
removeTask={removeTask}
tasks={tasks}
updateTask={updateTask}
/>
)
const mapStateToProps = state => ({
tasks: getVisibleTasks(state.get(reducerTodo)),
});
app/components/TodoList/TaskList.js
const taskItems = tasks.map((task, index) => (
<TaskItem
removeTask={removeTask}
key={index.toString()}
task={task}
updateTask={updateTask}
/>
));
app\containers\SampleFullstackApps\Todo\reducers\todoConstants.js
export const CREATE_TASK = 'CREATE_TASK';
export const CREATE_TASK_FAILED = 'CREATE_TASK_FAILED';
export const CREATE_TASK_FULFILLED = 'CREATE_TASK_FULFILLED';
\app\containers\SampleFullstackApps\Todo\reducers\todoActions.js
export const createTaskAction = title => ({
type: types.CREATE_TASK,
payload: { task: { title, completed: false } }
});
export const createTaskFailed = error => ({
type: types.CREATE_TASK_FAILED,
payload: { error }
});
export const createTaskFulfilled = task => ({
type: types.CREATE_TASK_FULFILLED,
payload: { task }
});
app\containers\SampleFullstackApps\Todo\reducers\todoSagas.js
const createTask = write.bind(null, taskList, taskList.push, createTaskFailed);
function* watchCreateTask() {
while (true) {
const { payload } = yield take(CREATE_TASK);
yield fork(createTask, payload.task);
}
}
app\containers\SampleFullstackApps\Todo\reducers\todoReducer.js
case CREATE_TASK_FULFILLED:
return state.set('list', state.list.unshift(payload.task));
app/containers/SampleFullstackApps/Todo/index.js
import { createTaskAction } from './reducers/todoActions';
const mapDispatchToProps = dispatch => ({
createTask: bindActionCreators(createTaskAction, dispatch),
});
<TaskForm handleSubmit={createTask} />
\app\components\TodoList\TaskForm.js
handleSubmit(event) {
event.preventDefault();
const { title } = this.state;
const { handleSubmit } = this.props;
const titleString = title.trim();
if (titleString.length) handleSubmit(titleString);
this.clearInput();
}
return (
<form onSubmit={this.handleSubmit} noValidate>
...
</form>
)
app\containers\SampleFullstackApps\Todo\reducers\todoConstants.js
export const UPDATE_TASK = 'UPDATE_TASK';
export const UPDATE_TASK_FAILED = 'UPDATE_TASK_FAILED';
export const UPDATE_TASK_FULFILLED = 'UPDATE_TASK_FULFILLED';
\app\containers\SampleFullstackApps\Todo\reducers\todoActions.js
export const updateTaskAction = (task, changes) => ({
type: types.UPDATE_TASK,
payload: { task, changes }
});
export const updateTaskFailed = error => ({
type: types.UPDATE_TASK_FAILED,
payload: { error }
});
export const updateTaskFulfilled = task => ({
type: types.UPDATE_TASK_FULFILLED,
payload: { task }
});
app\containers\SampleFullstackApps\Todo\reducers\todoSagas.js
const updateTask = write.bind(null, taskList, taskList.update, updateTaskFailed);
function* watchUpdateTask() {
while (true) {
const { payload } = yield take(UPDATE_TASK);
yield fork(updateTask, payload.task.key, payload.changes);
}
}
app\containers\SampleFullstackApps\Todo\reducers\todoReducer.js
case UPDATE_TASK_FULFILLED:
return state.set('list', state.list.map(task => (task.key === payload.task.key ? payload.task : task)));
app/containers/SampleFullstackApps/Todo/index.js
import { updateTaskAction } from './reducers/todoActions';
const mapDispatchToProps = dispatch => ({
updateTask: bindActionCreators(updateTaskAction, dispatch),
});
return (
<TaskList
loading={loading}
removeTask={removeTask}
tasks={tasks}
updateTask={updateTask}
/>
)
app\components\TodoList\TaskItem.js
save(event) {
const { editing } = this.state;
const { updateTask } = this.props;
if (editing) {
const { task } = this.props;
const title = event.target.value.trim();
if (title.length && title !== task.title) {
updateTask(task, { title });
}
this.stopEditing();
}
}
return (
<input
autoFocus // eslint-disable-line
autoComplete="off"
defaultValue={task.title}
maxLength="64"
onKeyUp={this.handleKeyUp}
type="text"
/>
)
app\containers\SampleFullstackApps\Todo\reducers\todoConstants.js
export const REMOVE_TASK = 'REMOVE_TASK';
export const REMOVE_TASK_FAILED = 'REMOVE_TASK_FAILED';
export const REMOVE_TASK_FULFILLED = 'REMOVE_TASK_FULFILLED';
\app\containers\SampleFullstackApps\Todo\reducers\todoActions.js
export const removeTaskAction = task => ({
type: types.REMOVE_TASK,
payload: { task }
});
export const removeTaskFailed = error => ({
type: types.REMOVE_TASK_FAILED,
payload: { error }
});
export const removeTaskFulfilled = task => ({
type: types.REMOVE_TASK_FULFILLED,
payload: { task }
});
app\containers\SampleFullstackApps\Todo\reducers\todoSagas.js
const removeTask = write.bind(null, taskList, taskList.remove, removeTaskFailed);
function* watchRemoveTask() {
while (true) {
const { payload } = yield take(REMOVE_TASK);
yield fork(removeTask, payload.task.key);
}
}
app\containers\SampleFullstackApps\Todo\reducers\todoReducer.js
case REMOVE_TASK_FULFILLED:
return state.set('list', state.list.filter(task => task.key !== payload.task.key));
app/containers/SampleFullstackApps/Todo/index.js
import { removeTaskAction } from './reducers/todoActions';
const mapDispatchToProps = dispatch => ({
removeTask: bindActionCreators(removeTaskAction, dispatch),
});
return (
<TaskList
loading={loading}
removeTask={removeTask}
tasks={tasks}
updateTask={updateTask}
/>
)
app\components\TodoList\TaskItem.js
remove() {
const { removeTask, task } = this.props;
removeTask(task);
this.setState({ anchorEl: null });
}
return (
<IconButton
className={
classNames(
classes.button,
editing && classes.hide
)
}
size="small"
onClick={this.remove}
>
<DeleteIcon />
</IconButton>
)