Created
July 30, 2020 21:59
-
-
Save zaydek/bb04045b8ec84f6a4b45eeac26d14580 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react" | |
import uuid from "uuid/v4" | |
import { useImmerReducer } from "use-immer" | |
// Asserts on undefined, false, -1, null. | |
function assert(value) { | |
if (value !== undefined && value !== false && value !== -1 && value !== null) { | |
return | |
} | |
throw new Error(`assert: value assertion; value=${value}`) | |
} | |
const initialState = { | |
todo: { | |
done: false, | |
text: "", | |
}, | |
todos: [], | |
} | |
// Toggles the next todo. | |
function toggleNextTodo(state) { | |
state.todo.done = !state.todo.done | |
} | |
// Updates the next todo. | |
function updateNextTodo(state, text) { | |
state.todo.text = text | |
} | |
/// Append the next todo (appends to the start). | |
function appendNextTodo(state) { | |
if (!state.todo.text) { | |
return | |
} | |
state.todos.unshift({ | |
id: uuid(), | |
...state.todo, | |
}) | |
state.todo.text = "" | |
} | |
// Toggles an todo based on an ID. | |
function toggleTodoByID(state, id) { | |
const todo = state.todos.find(each => each.id === id) | |
assert(todo) | |
todo.done = !todo.done | |
} | |
// Updates an todo based on an ID. | |
function updateTodoByID(state, id, text) { | |
const todo = state.todos.find(each => each.id === id) | |
assert(todo) | |
todo.text = text | |
} | |
// Deletes an todo based on an ID. | |
function deleteTodoByID(state, id) { | |
const x = state.todos.findIndex(each => each.id === id) | |
assert(x) | |
state.todos.splice(x, 1) | |
} | |
function TodoAppReducer(state, action) { | |
switch (action.type) { | |
case "TOGGLE_NEXT_TODO": | |
return void toggleNextTodo(state) | |
case "UPDATE_NEXT_TODO": | |
return void updateNextTodo(state, action.text) | |
case "APPEND_NEXT_TODO": | |
return void appendNextTodo(state) | |
case "TOGGLE_TODO_BY_ID": | |
return void toggleTodoByID(state, action.id) | |
case "UPDATE_TODO_BY_ID": | |
return void updateTodoByID(state, action.id, action.text) | |
case "DELETE_TODO_BY_ID": | |
return void deleteTodoByID(state, action.id) | |
default: | |
throw new Error(`TodoAppReducer: action mismatch; action=${action}`) | |
} | |
} | |
// NOTE: Uses React.memo because of dispatch. | |
const MemoTodoItem = React.memo(({ todo, dispatch }) => ( | |
<div id={todo.id}> | |
<input | |
type="checkbox" | |
checked={todo.done} | |
onChange={e => { | |
dispatch({ | |
type: "TOGGLE_TODO_BY_ID", | |
id: todo.id, | |
}) | |
}} | |
/> | |
<input | |
type="text" | |
value={todo.text} | |
onChange={e => { | |
dispatch({ | |
type: "UPDATE_TODO_BY_ID", | |
id: todo.id, | |
text: e.target.value, | |
}) | |
}} | |
/> | |
<button | |
onClick={e => { | |
dispatch({ | |
type: "DELETE_TODO_BY_ID", | |
id: todo.id, | |
}) | |
}} | |
> | |
Delete | |
</button> | |
</div> | |
)) | |
// NOTE: Uses React.memo because of dispatch. | |
const TodoForm = React.memo(({ form, dispatch }) => ( | |
<form onSubmit={e => { | |
e.preventDefault() | |
dispatch({ | |
type: "APPEND_NEXT_TODO", | |
}) | |
}}> | |
<input | |
type="checkbox" | |
checked={form.done} | |
onChange={e => { | |
dispatch({ | |
type: "TOGGLE_NEXT_TODO", | |
}) | |
}} | |
/> | |
<input | |
type="text" | |
value={form.text} | |
onChange={e => { | |
dispatch({ | |
type: "UPDATE_NEXT_TODO", | |
text: e.target.value, | |
}) | |
}} | |
/> | |
<button type="submit"> | |
Add | |
</button> | |
</form> | |
)) | |
const TodoApp = () => { | |
const [state, dispatch] = useImmerReducer(TodoAppReducer, {}, () => initialState) | |
return ( | |
<div> | |
<TodoForm form={state.todo} dispatch={dispatch} /> | |
{state.todos.map(each => ( | |
<MemoTodoItem key={each.id} todo={each} dispatch={dispatch} /> | |
))} | |
{/* Debugger */} | |
<pre style={{ tabSize: 2 }}> | |
{JSON.stringify(state, null, "\t")} | |
</pre> | |
</div> | |
) | |
} | |
export default TodoApp |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment