better-preact-use-state 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -47
- package/dist/use-state.d.mts.map +1 -1
- package/dist/use-state.mjs +6 -2
- package/dist/use-state.mjs.map +1 -1
- package/package.json +32 -23
- package/src/use-state.mts +7 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A utility that improves Preact's `useState` to be safer and more convenient. Pro
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
Preact's `useState`
|
|
7
|
+
Preact's `useState` hands back a single setter that accepts either the next value or an updater function, so the two cannot be told apart by type. `better-preact-use-state` is a utility designed for safer state management: its `setState` accepts only the next value, and it also hands back `updateState` and `resetState`. It is designed as a drop-in replacement for Preact's `useState` hook, with added functionality. `better-preact-use-state` also provides a simplified hook, `useBoolState`, specifically for boolean state.
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -30,27 +30,34 @@ pnpm add better-preact-use-state
|
|
|
30
30
|
|
|
31
31
|
```tsx
|
|
32
32
|
import { useState } from 'better-preact-use-state';
|
|
33
|
+
import type * as Preact from 'preact';
|
|
34
|
+
import { useCallback } from 'preact/hooks';
|
|
33
35
|
|
|
34
|
-
const MyComponent
|
|
35
|
-
const [
|
|
36
|
+
export const MyComponent = (): Preact.JSX.Element => {
|
|
37
|
+
const [userName, setUserName] = useState('John Doe');
|
|
36
38
|
|
|
37
|
-
const [count,
|
|
39
|
+
const [count, , { updateState: updateCount }] = useState(0);
|
|
38
40
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
const onNameInput: Preact.InputEventHandler<HTMLInputElement> = useCallback(
|
|
42
|
+
(ev) => {
|
|
43
|
+
setUserName(ev.currentTarget.value);
|
|
44
|
+
},
|
|
45
|
+
[setUserName],
|
|
46
|
+
);
|
|
42
47
|
|
|
43
48
|
const incrementCount = useCallback(() => {
|
|
44
49
|
updateCount((x) => x + 1);
|
|
45
|
-
}, []);
|
|
50
|
+
}, [updateCount]);
|
|
46
51
|
|
|
47
52
|
return (
|
|
48
53
|
<div>
|
|
49
54
|
<p>{`Count: ${count}`}</p>
|
|
50
|
-
<button onClick={incrementCount}>
|
|
55
|
+
<button type={'button'} onClick={incrementCount}>
|
|
56
|
+
{'Increment'}
|
|
57
|
+
</button>
|
|
51
58
|
|
|
52
|
-
<p>{`Name: ${
|
|
53
|
-
<input type=
|
|
59
|
+
<p>{`Name: ${userName}`}</p>
|
|
60
|
+
<input type={'text'} value={userName} onInput={onNameInput} />
|
|
54
61
|
</div>
|
|
55
62
|
);
|
|
56
63
|
};
|
|
@@ -62,18 +69,25 @@ const MyComponent: FunctionalComponent = () => {
|
|
|
62
69
|
|
|
63
70
|
```tsx
|
|
64
71
|
import { useBoolState } from 'better-preact-use-state';
|
|
72
|
+
import type * as Preact from 'preact';
|
|
65
73
|
|
|
66
|
-
const MyComponent = () => {
|
|
67
|
-
const [isOpen, { setTrue:
|
|
74
|
+
export const MyComponent = (): Preact.JSX.Element => {
|
|
75
|
+
const [isOpen, { setTrue: openPanel, setFalse: closePanel, toggleState }] =
|
|
68
76
|
useBoolState(false);
|
|
69
77
|
|
|
70
78
|
return (
|
|
71
79
|
<div>
|
|
72
80
|
<p>{`Is Open: ${isOpen ? 'Yes' : 'No'}`}</p>
|
|
73
|
-
<button
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
<button type={'button'} onClick={openPanel}>
|
|
82
|
+
{'Open'}
|
|
83
|
+
</button>
|
|
84
|
+
<button type={'button'} onClick={closePanel}>
|
|
85
|
+
{'Close'}
|
|
86
|
+
</button>
|
|
76
87
|
{/* Toggles the boolean value */}
|
|
88
|
+
<button type={'button'} onClick={toggleState}>
|
|
89
|
+
{'Toggle'}
|
|
90
|
+
</button>
|
|
77
91
|
</div>
|
|
78
92
|
);
|
|
79
93
|
};
|
|
@@ -83,8 +97,8 @@ const MyComponent = () => {
|
|
|
83
97
|
|
|
84
98
|
### `useState`
|
|
85
99
|
|
|
86
|
-
```
|
|
87
|
-
const [state, setState, { updateState, resetState }] = useState
|
|
100
|
+
```ts
|
|
101
|
+
const [state, setState, { updateState, resetState }] = useState(initialState);
|
|
88
102
|
```
|
|
89
103
|
|
|
90
104
|
- `state`: The current state.
|
|
@@ -94,24 +108,27 @@ const [state, setState, { updateState, resetState }] = useState<T>(initialState:
|
|
|
94
108
|
|
|
95
109
|
#### `updateState`
|
|
96
110
|
|
|
97
|
-
```
|
|
98
|
-
updateState(updateFn: (v: T) => T)
|
|
111
|
+
```ts
|
|
112
|
+
updateState: (updateFn: (v: T) => T) => void;
|
|
99
113
|
```
|
|
100
114
|
|
|
101
115
|
Updates the state by passing a function `updateFn` that takes the current state as an argument and returns the new state.
|
|
102
116
|
|
|
103
117
|
#### `resetState`
|
|
104
118
|
|
|
105
|
-
```
|
|
106
|
-
resetState()
|
|
119
|
+
```ts
|
|
120
|
+
resetState: () => void;
|
|
107
121
|
```
|
|
108
122
|
|
|
109
|
-
Resets the state to the
|
|
123
|
+
Resets the state to the value it was created with: the first render's `initialState`, as `useState` reads it, whatever is passed later. Its identity never changes.
|
|
110
124
|
|
|
111
125
|
### `useBoolState`
|
|
112
126
|
|
|
113
|
-
```
|
|
114
|
-
const [
|
|
127
|
+
```ts
|
|
128
|
+
const [
|
|
129
|
+
state,
|
|
130
|
+
{ setState, setTrue, setFalse, resetState, toggleState, updateState },
|
|
131
|
+
] = useBoolState(initialState);
|
|
115
132
|
```
|
|
116
133
|
|
|
117
134
|
- `state`: The current boolean value.
|
|
@@ -124,67 +141,66 @@ const [state, { setState, setTrue, setFalse, resetState, toggleState, updateStat
|
|
|
124
141
|
|
|
125
142
|
#### `setState`
|
|
126
143
|
|
|
127
|
-
```
|
|
128
|
-
setState(next: boolean)
|
|
144
|
+
```ts
|
|
145
|
+
setState: (next: boolean) => void;
|
|
129
146
|
```
|
|
130
147
|
|
|
131
148
|
Sets the state to the provided boolean value.
|
|
132
149
|
|
|
133
150
|
#### `setTrue`
|
|
134
151
|
|
|
135
|
-
```
|
|
136
|
-
setTrue()
|
|
152
|
+
```ts
|
|
153
|
+
setTrue: () => void;
|
|
137
154
|
```
|
|
138
155
|
|
|
139
156
|
Sets the state to `true`.
|
|
140
157
|
|
|
141
158
|
#### `setFalse`
|
|
142
159
|
|
|
143
|
-
```
|
|
144
|
-
setFalse()
|
|
160
|
+
```ts
|
|
161
|
+
setFalse: () => void;
|
|
145
162
|
```
|
|
146
163
|
|
|
147
164
|
Sets the state to `false`.
|
|
148
165
|
|
|
149
166
|
#### `resetState`
|
|
150
167
|
|
|
151
|
-
```
|
|
152
|
-
resetState()
|
|
168
|
+
```ts
|
|
169
|
+
resetState: () => void;
|
|
153
170
|
```
|
|
154
171
|
|
|
155
|
-
Resets the state to the
|
|
172
|
+
Resets the state to the value it was created with: the first render's `initialState`, as `useState` reads it, whatever is passed later. Its identity never changes.
|
|
156
173
|
|
|
157
174
|
#### `toggleState`
|
|
158
175
|
|
|
159
|
-
```
|
|
160
|
-
toggleState()
|
|
176
|
+
```ts
|
|
177
|
+
toggleState: () => void;
|
|
161
178
|
```
|
|
162
179
|
|
|
163
180
|
Toggles the current boolean value.
|
|
164
181
|
|
|
165
182
|
#### `updateState`
|
|
166
183
|
|
|
167
|
-
```
|
|
168
|
-
updateState(updateFn: (v: boolean) => boolean)
|
|
184
|
+
```ts
|
|
185
|
+
updateState: (updateFn: (v: boolean) => boolean) => void;
|
|
169
186
|
```
|
|
170
187
|
|
|
171
188
|
Updates the state by passing a function `updateFn` that takes the current state as an argument and returns the new state. This is useful for updates that depend on the current state.
|
|
172
189
|
|
|
173
190
|
## Benefits of this library
|
|
174
191
|
|
|
175
|
-
With Preact's standard `useState`,
|
|
176
|
-
|
|
177
|
-
### Example Use Case
|
|
192
|
+
With Preact's standard `useState`, `setState` accepts either the next value or an updater function, and nothing in the types tells the two apart. `better-preact-use-state` separates them: `setState` is typed to accept only the next value, and an update that depends on the current value goes through `updateState` explicitly. `resetState` lets you easily return the state to its initial value. `useBoolState` simplifies boolean state management in Preact, offering several advantages over directly using `useState` for booleans.
|
|
178
193
|
|
|
179
|
-
|
|
194
|
+
### Holding a function as state
|
|
180
195
|
|
|
181
|
-
|
|
182
|
-
const [fn, setFn, { updateState: updateFn }] = useState(initialFn); // Holds a function as state
|
|
196
|
+
The initial value and the argument of `setState` are passed to Preact's `useState` as they are, so a function given to either is still called as a lazy initializer or an updater, just as with Preact's own hook. To hold a function as state, wrap it in an object:
|
|
183
197
|
|
|
184
|
-
|
|
198
|
+
```ts
|
|
199
|
+
const [{ fn }, setFn] = useState({ fn: initialFn });
|
|
185
200
|
|
|
186
|
-
|
|
187
|
-
|
|
201
|
+
const onReplace = useCallback((): void => {
|
|
202
|
+
setFn({ fn: nextFn });
|
|
203
|
+
}, [setFn, nextFn]);
|
|
188
204
|
```
|
|
189
205
|
|
|
190
206
|
## License
|
package/dist/use-state.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-state.d.mts","sourceRoot":"","sources":["../src/use-state.mts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ,GAAI,CAAC,gBACV,CAAC,KACd,SAAS,CACV,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,EAC3B,OAAO,EAAE,QAAQ,CAAC;IAChB,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;IAC7C,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC,
|
|
1
|
+
{"version":3,"file":"use-state.d.mts","sourceRoot":"","sources":["../src/use-state.mts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ,GAAI,CAAC,gBACV,CAAC,KACd,SAAS,CACV,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,EAC3B,OAAO,EAAE,QAAQ,CAAC;IAChB,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;IAC7C,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC,CAkBH,CAAC"}
|
package/dist/use-state.mjs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import * as Preact from 'preact/hooks';
|
|
2
2
|
export const useState = (initialState) => {
|
|
3
3
|
const [state, setState] = Preact.useState(initialState);
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const [initial] = Preact.useState(initialState);
|
|
4
8
|
const resetState = Preact.useCallback(() => {
|
|
5
|
-
setState(() =>
|
|
6
|
-
}, [
|
|
9
|
+
setState(() => initial);
|
|
10
|
+
}, [initial]);
|
|
7
11
|
const updateState = Preact.useCallback((updateFn) => {
|
|
8
12
|
setState(updateFn);
|
|
9
13
|
}, []);
|
package/dist/use-state.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-state.mjs","sourceRoot":"","sources":["../src/use-state.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AAEvC,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,YAAe,EAQf,EAAE;IACF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAI,YAAY,CAAC,CAAC;IAE3D,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE;QACzC,QAAQ,CAAC,GAAG,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"use-state.mjs","sourceRoot":"","sources":["../src/use-state.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AAEvC,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,YAAe,EAQf,EAAE;IACF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAI,YAAY,CAAC,CAAC;IAE3D,6EAA6E;IAC7E,0EAA0E;IAC1E,uEAAuE;IACvE,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAI,YAAY,CAAC,CAAC;IAEnD,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE;QACzC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,QAAqB,EAAE,EAAE;QAC/D,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;AACxD,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-preact-use-state",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "useState that also hands back the setters you would otherwise write by hand: updateState, resetState, and setTrue / setFalse / toggleState for booleans.",
|
|
6
6
|
"keywords": [
|
|
@@ -33,24 +33,27 @@
|
|
|
33
33
|
"LICENSE"
|
|
34
34
|
],
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"eslint
|
|
39
|
-
"eslint-
|
|
40
|
-
"
|
|
36
|
+
"@testing-library/preact": "3.2.4",
|
|
37
|
+
"@vitest/browser-playwright": "5.0.5",
|
|
38
|
+
"eslint": "10.13.0",
|
|
39
|
+
"eslint-config-typed": "7.14.0",
|
|
40
|
+
"eslint-plugin-ts-data-forge": "0.13.1",
|
|
41
|
+
"eslint-plugin-ts-type-forge": "0.8.26",
|
|
42
|
+
"npm-run-all2": "9.0.4",
|
|
43
|
+
"playwright": "1.64.1",
|
|
41
44
|
"preact": "10.29.8",
|
|
42
|
-
"prettier": "3.9.
|
|
45
|
+
"prettier": "3.9.10",
|
|
43
46
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
44
47
|
"prettier-plugin-packagejson": "3.0.2",
|
|
45
|
-
"ts-data-forge": "
|
|
46
|
-
"ts-repo-utils": "
|
|
47
|
-
"tsx": "4.23.
|
|
48
|
+
"ts-data-forge": "15.0.1",
|
|
49
|
+
"ts-repo-utils": "11.0.1",
|
|
50
|
+
"tsx": "4.23.15"
|
|
48
51
|
},
|
|
49
52
|
"peerDependencies": {
|
|
50
53
|
"preact": ">=10.0.0"
|
|
51
54
|
},
|
|
52
55
|
"engines": {
|
|
53
|
-
"node": ">=22.
|
|
56
|
+
"node": ">=22.23.4",
|
|
54
57
|
"pnpm": ">=9.0.0"
|
|
55
58
|
},
|
|
56
59
|
"publishConfig": {
|
|
@@ -59,18 +62,24 @@
|
|
|
59
62
|
},
|
|
60
63
|
"scripts": {
|
|
61
64
|
"build": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/build.mts",
|
|
62
|
-
"build:min": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/build.mts --skip-check",
|
|
63
65
|
"check:ext": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-ext.mts",
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
66
|
+
"check:lint": "node ../../tools/scripts/cmd/assert-deps-built.mts && oxlint . && eslint .",
|
|
67
|
+
"check:test": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest:node run",
|
|
68
|
+
"check:test:browser": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest --project='Browser' run",
|
|
69
|
+
"check:types": "node ../../tools/scripts/cmd/assert-deps-built.mts && node ../../node_modules/typescript-native/bin/tsc --noEmit",
|
|
70
|
+
"doc": "run-s gen:readme",
|
|
71
|
+
"fix:fmt": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-uncommitted.mts --cwd .",
|
|
72
|
+
"fix:fmt:diff": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-diff-from.mts origin/main --cwd .",
|
|
73
|
+
"fix:fmt:full": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../tools/scripts/cmd/format-package.mts",
|
|
74
|
+
"fix:lint": "node ../../tools/scripts/cmd/assert-deps-built.mts && oxlint . --fix && eslint . --fix",
|
|
75
|
+
"fmt": "pnpm run fix:fmt",
|
|
76
|
+
"gen": "run-s gen:index gen:readme",
|
|
77
|
+
"gen:index": "run-s gen:index:src fix:fmt",
|
|
78
|
+
"gen:index:src": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/gen-index-ts.mts ./src --index-ext .mts --export-ext .mjs --target-ext .mts",
|
|
79
|
+
"gen:readme": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/embed-examples.mts",
|
|
80
|
+
"test": "pnpm run check:test",
|
|
81
|
+
"watch:types": "node ../../node_modules/typescript-native/bin/tsc --noEmit --watch -p ./tsconfig.json",
|
|
82
|
+
"z:vitest": "vitest --config ./configs/vitest.config.mts",
|
|
83
|
+
"z:vitest:node": "pnpm run z:vitest --project='Node.js'"
|
|
75
84
|
}
|
|
76
85
|
}
|
package/src/use-state.mts
CHANGED
|
@@ -12,9 +12,14 @@ export const useState = <T,>(
|
|
|
12
12
|
] => {
|
|
13
13
|
const [state, setState] = Preact.useState<T>(initialState);
|
|
14
14
|
|
|
15
|
+
// The value the state was created with, read once as `useState` reads it, so
|
|
16
|
+
// that `resetState` neither follows a later argument nor changes identity
|
|
17
|
+
// when one is passed (an object literal is a new one on every render).
|
|
18
|
+
const [initial] = Preact.useState<T>(initialState);
|
|
19
|
+
|
|
15
20
|
const resetState = Preact.useCallback(() => {
|
|
16
|
-
setState(() =>
|
|
17
|
-
}, [
|
|
21
|
+
setState(() => initial);
|
|
22
|
+
}, [initial]);
|
|
18
23
|
|
|
19
24
|
const updateState = Preact.useCallback((updateFn: (v: T) => T) => {
|
|
20
25
|
setState(updateFn);
|