diff --git a/src/strategies/index.ts b/src/strategies/index.ts index 1253936..c6a5992 100644 --- a/src/strategies/index.ts +++ b/src/strategies/index.ts @@ -1,3 +1,7 @@ +import { UseReducer } from "./useReducer"; import { UseState } from "./useState"; -export const strategies = [{ name: 'useState', component: UseState }] +export const strategies = [ + { name: 'useState', component: UseState }, + { name: 'useReducer', component: UseReducer }, +] diff --git a/src/strategies/useReducer.tsx b/src/strategies/useReducer.tsx new file mode 100644 index 0000000..d53cfd3 --- /dev/null +++ b/src/strategies/useReducer.tsx @@ -0,0 +1,145 @@ +import React, { useEffect, useReducer, useState } from 'react' +import localforage from 'localforage' +import { useAsyncValue } from 'react-router' + +type Todo = { + id: string + text: string + status: 'incomplete' | 'complete' +} + +type TodoAction = + | { + type: 'delete' + value: string + } + | { + type: 'add' + value: Todo + } + | { + type: 'replace' + value: Todo[] + } + | { + type: 'update' + value: Todo + } +const reducer = (state: Todo[], action: TodoAction) => { + const { type, value } = action + switch (type) { + case 'delete': + return state.filter((todo) => todo.id !== value) + case 'add': + return state.concat([value]) + case 'replace': + return value + case 'update': + return state.map((todo) => (todo.id === value.id ? value : todo)) + } +} + +export function UseReducer() { + const [isLoading, setLoading] = useState(true) + const [todos, dispatchTodoAction] = useReducer(reducer, []) + const [newTodoText, setNewTodoText] = useState('') + + useEffect(() => { + // run once when mounted + localforage.getItem('react-state-management/todos', (_err, value) => { + if (value) { + dispatchTodoAction({ type: 'replace', value: value as Todo[] }) // validation first would be better + } + setLoading(false) + }) + }, []) + + useEffect(() => { + // keep local db up to date + localforage.setItem('react-state-management/todos', todos) + }, [todos]) + + function addTodo() { + const newTodo = { + id: crypto.randomUUID(), + text: newTodoText, + status: 'incomplete' as const + } + dispatchTodoAction({ + type: 'add', + value: newTodo + }) + setNewTodoText('') + } + + return ( +
+ +
{ + e.preventDefault() + addTodo() + }}> + +
+
+ ) +} + +function TodoList({ + todos, + dispatch +}: { + todos: Todo[] + dispatch: (TodoAction) => void +}) { + return ( + <> + {todos.map((todo) => ( + + ))} + + ) +} + +function TodoItem({ + todo, + dispatch +}: { + todo: Todo + dispatch: (TodoAction) => void +}) { + return ( + + ) +} diff --git a/src/strategies/useState.tsx b/src/strategies/useState.tsx index 51b2e04..0e7902f 100644 --- a/src/strategies/useState.tsx +++ b/src/strategies/useState.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import localforage from 'localforage' type Todo = { @@ -12,21 +12,20 @@ export function UseState() { const [todos, setTodos] = useState([]) const [newTodoText, setNewTodoText] = useState('') - localforage.getItem('react-state-management/todos', (_err, value) => { - if (value) { - setTodos(value as Todo[]) // validation first would be better - } - setLoading(false) - }) - - function updateTodos(newTodos: Todo[]) { - // alternative to tricky useEffect - setLoading(true) - setTodos(newTodos) - localforage.setItem('react-state-management/todos', newTodos).then(() => { + useEffect(() => { + // run once when mounted + localforage.getItem('react-state-management/todos', (_err, value) => { + if (value) { + setTodos(value as Todo[]) // validation first would be better + } setLoading(false) }) - } + }, []) + + useEffect(() => { + // keep local db up to date + localforage.setItem('react-state-management/todos', todos) + }, [todos]) function addTodo() { const newTodo = { @@ -36,11 +35,11 @@ export function UseState() { } const newTodos = [...todos, newTodo] setNewTodoText('') - updateTodos(newTodos) + setTodos(newTodos) } function todoSetter(id: string, newValue?: Todo) { - updateTodos( + setTodos( todos.reduce((acc, cur) => { if (cur.id === id) { if (newValue === undefined) {