From 60684dbd0a456a27e6c3bb54b6ac445158acaeec Mon Sep 17 00:00:00 2001 From: Joshua Seigler Date: Mon, 10 Oct 2022 16:31:33 -0400 Subject: [PATCH] add useContext --- src/App.tsx | 8 +- src/strategies/index.ts | 6 +- src/strategies/useContext.tsx | 148 ++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 src/strategies/useContext.tsx diff --git a/src/App.tsx b/src/App.tsx index c3e2591..9aeecd0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -52,7 +52,13 @@ function Introduction() { return (

Introduction

-

Here are some ways to store state in a React Application.

+

+ This repo has a simple ToDo app mostly using React's useState hook. +

+

+ We will rewrite this app with useReducer, Redux, useContext, and + something new, Preact signals. +

) } diff --git a/src/strategies/index.ts b/src/strategies/index.ts index b415bd6..90fe92c 100644 --- a/src/strategies/index.ts +++ b/src/strategies/index.ts @@ -1,9 +1,11 @@ -import { UseReducer } from "./useReducer"; -import { UseSignal } from "./useSignal"; import { UseState } from "./useState"; +import { UseReducer } from "./useReducer"; +import { UseContext } from "./useContext"; +import { UseSignal } from "./useSignal"; export const strategies = [ { name: 'useState', component: UseState }, { name: 'useReducer', component: UseReducer }, + { name: 'useContext', component: UseContext }, { name: 'useSignal', component: UseSignal }, ] diff --git a/src/strategies/useContext.tsx b/src/strategies/useContext.tsx new file mode 100644 index 0000000..79bdc99 --- /dev/null +++ b/src/strategies/useContext.tsx @@ -0,0 +1,148 @@ +import React, { useContext, useEffect, useReducer, useState } from 'react' +import localforage from 'localforage' + +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)) + } +} + +const TodoContext = React.createContext({ + todos: [] as Todo[], + dispatchTodoAction: (TodoAction) => {} +}) + +export function UseContext() { + 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() { + const { todos } = useContext(TodoContext) + return ( + <> + {todos.map((todo) => ( + + ))} + + ) +} + +function TodoItem({ + todo +}: { + todo: Todo +}) { + const { dispatchTodoAction: dispatch } = useContext(TodoContext) + return ( + + ) +}