From 17ec89d88635f63f9fbacd600531fd35295d40fe Mon Sep 17 00:00:00 2001 From: Joshua Seigler Date: Mon, 10 Oct 2022 02:15:34 -0400 Subject: [PATCH] useSignal demo, wow --- package-lock.json | 38 +++++++++++++ package.json | 1 + src/strategies/index.ts | 2 + src/strategies/useSignal.tsx | 103 +++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 src/strategies/useSignal.tsx diff --git a/package-lock.json b/package-lock.json index ae6e889..d8d5131 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@preact/signals-react": "^1.1.1", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", "@types/react-router-dom": "^5.3.3", @@ -1429,6 +1430,30 @@ "@parcel/core": "^2.7.0" } }, + "node_modules/@preact/signals-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.2.1.tgz", + "integrity": "sha512-aqzRMNFU1hoQyP4Kb1ldJrUTCnA9vqPDa7qHEQzHJ3upnBOcC2pjmvjAuTqGuY4AVTtUkCQV0FvOCuIQQ2hSdA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@preact/signals-react": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@preact/signals-react/-/signals-react-1.1.1.tgz", + "integrity": "sha512-U5HNWBt4q5pmsZjDOuVcz3OXLQtaBMMSErnTHFohOFQClBqHlVD/hmhayEEO38I9iU71kofhw2ngeWso/GsVMw==", + "dependencies": { + "@preact/signals-core": "^1.2.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "react": "17.x || 18.x" + } + }, "node_modules/@remix-run/router": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz", @@ -3758,6 +3783,19 @@ "nullthrows": "^1.1.1" } }, + "@preact/signals-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.2.1.tgz", + "integrity": "sha512-aqzRMNFU1hoQyP4Kb1ldJrUTCnA9vqPDa7qHEQzHJ3upnBOcC2pjmvjAuTqGuY4AVTtUkCQV0FvOCuIQQ2hSdA==" + }, + "@preact/signals-react": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@preact/signals-react/-/signals-react-1.1.1.tgz", + "integrity": "sha512-U5HNWBt4q5pmsZjDOuVcz3OXLQtaBMMSErnTHFohOFQClBqHlVD/hmhayEEO38I9iU71kofhw2ngeWso/GsVMw==", + "requires": { + "@preact/signals-core": "^1.2.1" + } + }, "@remix-run/router": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz", diff --git a/package.json b/package.json index b0ecc07..42ae765 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "author": "Joshua Seigler", "license": "MIT", "dependencies": { + "@preact/signals-react": "^1.1.1", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", "@types/react-router-dom": "^5.3.3", diff --git a/src/strategies/index.ts b/src/strategies/index.ts index c6a5992..b415bd6 100644 --- a/src/strategies/index.ts +++ b/src/strategies/index.ts @@ -1,7 +1,9 @@ import { UseReducer } from "./useReducer"; +import { UseSignal } from "./useSignal"; import { UseState } from "./useState"; export const strategies = [ { name: 'useState', component: UseState }, { name: 'useReducer', component: UseReducer }, + { name: 'useSignal', component: UseSignal }, ] diff --git a/src/strategies/useSignal.tsx b/src/strategies/useSignal.tsx new file mode 100644 index 0000000..137f75d --- /dev/null +++ b/src/strategies/useSignal.tsx @@ -0,0 +1,103 @@ +import React, { useEffect } from 'react' +import localforage from 'localforage' +import { effect, signal, useSignalEffect } from '@preact/signals-react' + +type Todo = { + id: string + text: string + status: 'incomplete' | 'complete' +} + +const isLoading = signal(true) +const todos = signal([]) +effect(() => { + if (!isLoading.value) { + localforage.setItem('react-state-management/todos', todos.value) + } +}) +const newTodoText = signal('') + +function addTodo() { + const newTodo = { + id: crypto.randomUUID(), + text: newTodoText.value, + status: 'incomplete' as const + } + todos.value = todos.value.concat([newTodo]) + newTodoText.value = '' +} + +function removeTodo(id: string) { + todos.value = todos.value.filter((t) => t.id !== id) +} + +function updateTodo(value: Todo) { + todos.value = todos.value.map((t) => (t.id === value.id ? value : t)) +} + +export function UseSignal() { + useEffect(() => { + // run once when mounted + localforage.getItem('react-state-management/todos', (_err, value) => { + isLoading.value = false + if (value) { + todos.value = value as Todo[] // validation first would be better + } + }) + }, []) + + return ( +
+ +
{ + e.preventDefault() + addTodo() + }}> + +
+
+ ) +} + +function TodoList() { + return ( + <> + {todos.value.map((todo) => ( + + ))} + + ) +} + +function TodoItem({ todo }: { todo: Todo }) { + return ( + + ) +}