Skip to content

Instantly share code, notes, and snippets.

@zaydek
Last active August 8, 2021 22:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zaydek/683663332da4bb3c00e2976262e8659a to your computer and use it in GitHub Desktop.
Save zaydek/683663332da4bb3c00e2976262e8659a to your computer and use it in GitHub Desktop.
function newTodo({ checked, value } = {}) {
const id = Math.random().toString(36).slice(2, 8)
checked ??= false
value ??= ""
return Todo({
id,
checked,
value,
})
}
function Todo(props) {
return {
$$type: "TODO",
checked: false,
value: "",
...props,
setChecked(checked) {
return Todo({
...props,
checked,
})
},
toggleChecked() {
return Todo({
...props,
checked: !props.checked,
})
},
setValue(value) {
return Todo({
...props,
value,
})
},
}
}
////////////////////////////////////////////////////////////////////////////////
function newTodoApp({ current } = {}) {
current ??= newTodo()
return TodoApp({
current,
todos: [],
})
}
function TodoApp(props) {
return {
$$type: "TODO_APP",
...props,
prepend() {
if (props.current.value === "") {
return TodoApp(props)
}
return TodoApp({
...props,
current: newTodo(),
todos: [
props.current,
...props.todos,
],
})
},
append() {
if (props.current.value === "") {
return TodoApp(props)
}
return TodoApp({
...props,
current: newTodo(),
todos: [
...props.todos,
props.current,
],
})
},
setCheckedByID(id, checked) {
const x = props.todos.findIndex(todo => todo.id === id)
if (x === -1) {
throw new Error("FIXME")
}
return TodoApp({
...props,
todos: [
...props.todos.slice(0, x),
props.todos[x].setChecked(checked),
...props.todos.slice(x + 1),
],
})
},
setValueByID(id, value) {
const x = props.todos.findIndex(todo => todo.id === id)
if (x === -1) {
throw new Error("FIXME")
}
return TodoApp({
...props,
todos: [
...props.todos.slice(0, x),
props.todos[x].setValue(value),
...props.todos.slice(x + 1),
],
})
},
removeByID(id) {
const x = props.todos.findIndex(todo => todo.id === id)
if (x === -1) {
throw new Error("FIXME")
}
return TodoApp({
...props,
todos: [
...props.todos.slice(0, x),
...props.todos.slice(x + 1),
],
})
},
}
}
////////////////////////////////////////////////////////////////////////////////
function reducer(todoApp, action) {
console.log(action.type)
switch (action.type) {
case "SET_CHECKED":
todoApp = TodoApp({
...todoApp,
current: todoApp.current.setChecked(action.data.checked),
})
break
case "SET_VALUE":
todoApp = TodoApp({
...todoApp,
current: todoApp.current.setValue(action.data.value),
})
break
case "PREPEND":
todoApp = todoApp.prepend(todoApp.current)
break
case "APPEND":
todoApp = todoApp.append(todoApp.current)
break
case "SET_CHECKED_BY_ID":
todoApp = todoApp.setCheckedByID(action.data.id, action.data.checked)
break
case "SET_VALUE_BY_ID":
todoApp = todoApp.setValueByID(action.data.id, action.data.value)
break
case "REMOVE_BY_ID":
todoApp = todoApp.removeByID(action.data.id)
break
}
return todoApp
}
export default function App() {
const [state, dispatch] = React.useReducer(reducer, null, () => newTodoApp())
return (
<div>
<form onSubmit={e => {
e.preventDefault()
dispatch({
type: "PREPEND"
})
}}>
{/* Current todo */}
<input
type="checkbox"
checked={state.current.checked}
onChange={e => {
dispatch({
type: "SET_CHECKED",
data: {
checked: e.target.checked,
},
})
}}
/>
<input
type="text"
value={state.current.value}
onChange={e => {
dispatch({
type: "SET_VALUE",
data: {
value: e.target.value,
},
})
}}
/>
<button type="submit">+</button>
</form>
{/* Todos */}
{state.todos.map(todo => (
<div key={todo.id}>
<input
type="checkbox"
checked={todo.checked}
onChange={e => {
dispatch({
type: "SET_CHECKED_BY_ID",
data: {
id: todo.id,
checked: e.target.checked,
},
})
}}
/>
<input
type="text"
value={todo.value}
onChange={e => {
dispatch({
type: "SET_VALUE_BY_ID",
data: {
id: todo.id,
value: e.target.value,
},
})
}}
/>
<button onClick={e => {
dispatch({
type: "REMOVE_BY_ID",
data: {
id: todo.id,
},
})
}}>
-
</button>
</div>
))}
<pre>
{JSON.stringify(state, null, 2)}
</pre>
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment