useSignal demo, wow

This commit is contained in:
Joshua Seigler 2022-10-10 02:15:34 -04:00
parent 31a9a1b463
commit 17ec89d886
4 changed files with 144 additions and 0 deletions

38
package-lock.json generated
View file

@ -8,6 +8,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@preact/signals-react": "^1.1.1",
"@types/react": "^18.0.21", "@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
@ -1429,6 +1430,30 @@
"@parcel/core": "^2.7.0" "@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": { "node_modules/@remix-run/router": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz",
@ -3758,6 +3783,19 @@
"nullthrows": "^1.1.1" "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": { "@remix-run/router": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz",

View file

@ -20,6 +20,7 @@
"author": "Joshua Seigler", "author": "Joshua Seigler",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@preact/signals-react": "^1.1.1",
"@types/react": "^18.0.21", "@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",

View file

@ -1,7 +1,9 @@
import { UseReducer } from "./useReducer"; import { UseReducer } from "./useReducer";
import { UseSignal } from "./useSignal";
import { UseState } from "./useState"; import { UseState } from "./useState";
export const strategies = [ export const strategies = [
{ name: 'useState', component: UseState }, { name: 'useState', component: UseState },
{ name: 'useReducer', component: UseReducer }, { name: 'useReducer', component: UseReducer },
{ name: 'useSignal', component: UseSignal },
] ]

View file

@ -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<Todo[]>([])
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 (
<main
style={isLoading.value ? { pointerEvents: 'none', cursor: 'wait' } : {}}>
<TodoList />
<form
onSubmit={(e) => {
e.preventDefault()
addTodo()
}}>
<label>
New todo:{' '}
<input
type="text"
value={newTodoText.value}
onChange={(e) => (newTodoText.value = e.target.value)}
/>
</label>
</form>
</main>
)
}
function TodoList() {
return (
<>
{todos.value.map((todo) => (
<TodoItem key={todo.id} todo={todo} />
))}
</>
)
}
function TodoItem({ todo }: { todo: Todo }) {
return (
<label
style={
todo.status === 'complete' ? { textDecoration: 'line-through' } : {}
}>
<a style={{ float: 'right' }} onClick={() => removeTodo(todo.id)}>
x
</a>
<input
type="checkbox"
onChange={(e) => {
updateTodo({
...todo,
status: e.target.checked ? 'complete' : 'incomplete'
})
}}
checked={todo.status === 'complete'}
/>{' '}
{todo.text}
</label>
)
}