@t8/react-pending 1.0.15 โ 1.0.17
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/LICENSE +21 -0
- package/README.md +35 -31
- package/dist/index.js +3 -5
- package/package.json +8 -9
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Alexander Tkaฤenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -8,46 +8,38 @@ No need to rearrange the app's shared state setup and to rewrite the async actio
|
|
|
8
8
|
|
|
9
9
|
Installation: `npm i @t8/react-pending`
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Example
|
|
12
12
|
|
|
13
13
|
Objective: Track the pending state of the async `fetchItems()` action to tell the user whether the UI is busy or encountered an error (preferably without rewriting the action and the app's state management).
|
|
14
14
|
|
|
15
15
|
```diff
|
|
16
16
|
+ import { usePendingState } from "@t8/react-pending";
|
|
17
17
|
|
|
18
|
-
const ItemList = () => {
|
|
18
|
+
export const ItemList = () => {
|
|
19
19
|
const [items, setItems] = useState([]);
|
|
20
|
-
// the custom string key parameter tags the action's state so
|
|
21
|
-
// that another component can access this state by the same tag
|
|
22
20
|
+ const [state, withState] = usePendingState("fetch-items");
|
|
23
21
|
|
|
24
22
|
useEffect(() => {
|
|
25
|
-
// wrapping fetchItems() to track the async action's state
|
|
26
23
|
- fetchItems().then(setItems);
|
|
27
24
|
+ withState(fetchItems()).then(setItems);
|
|
28
25
|
}, [fetchItems, withState]);
|
|
29
26
|
|
|
30
|
-
+ if (!state.complete)
|
|
31
|
-
+
|
|
32
|
-
|
|
33
|
-
+ if (state.error)
|
|
34
|
-
+ return <p>An error occurred</p>;
|
|
27
|
+
+ if (!state.complete) return <p>Loading...</p>;
|
|
28
|
+
+ if (state.error) return <p>An error occurred</p>;
|
|
35
29
|
|
|
36
30
|
return <ul>{items.map(/* ... */)}</ul>;
|
|
37
31
|
};
|
|
32
|
+
```
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
+ const [state] = usePendingState("fetch-items");
|
|
42
|
-
|
|
43
|
-
if (!state.initialized)
|
|
44
|
-
return "Initial";
|
|
34
|
+
```diff
|
|
35
|
+
+ import { usePendingState } from "@t8/react-pending";
|
|
45
36
|
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
export const Status = () => {
|
|
38
|
+
+ const [state] = usePendingState("fetch-items");
|
|
48
39
|
|
|
49
|
-
if (state.
|
|
50
|
-
|
|
40
|
+
if (!state.initialized) return "";
|
|
41
|
+
if (!state.complete) return "Busy";
|
|
42
|
+
if (state.error) return "Error";
|
|
51
43
|
|
|
52
44
|
return "OK";
|
|
53
45
|
};
|
|
@@ -55,37 +47,45 @@ Objective: Track the pending state of the async `fetchItems()` action to tell th
|
|
|
55
47
|
|
|
56
48
|
[Live demo](https://codesandbox.io/p/sandbox/rrr9cl?file=%2Fsrc%2FItemList.tsx)
|
|
57
49
|
|
|
58
|
-
๐น
|
|
50
|
+
๐น In this example, the action's value (the `items` array) is stored in the component's local state, but it can be stored in any app state of the developer's choice.
|
|
51
|
+
|
|
52
|
+
## Shared and local pending state
|
|
53
|
+
|
|
54
|
+
Omit the custom string key parameter of `usePendingState()` to scope the pending state locally within a single component:
|
|
59
55
|
|
|
60
56
|
```diff
|
|
61
|
-
- const [state, withState] = usePendingState("fetch-items");
|
|
62
|
-
+ const [state, withState] = usePendingState();
|
|
57
|
+
- const [state, withState] = usePendingState("fetch-items"); // shared
|
|
58
|
+
+ const [state, withState] = usePendingState(); // local
|
|
63
59
|
```
|
|
64
60
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
๐น Silently tracking the action's pending state, e.g. with background or optimistic updates (preventing `state.complete` from switching to `false` in the pending state):
|
|
61
|
+
## Silent tracking of background and optimistic updates
|
|
68
62
|
|
|
69
63
|
```diff
|
|
70
64
|
- withState(fetchItems())
|
|
71
65
|
+ withState(fetchItems(), { silent: true })
|
|
72
66
|
```
|
|
73
67
|
|
|
74
|
-
๐น
|
|
68
|
+
๐น This option prevents `state.complete` from switching to `false` in the pending state.
|
|
69
|
+
|
|
70
|
+
## Delayed pending state
|
|
75
71
|
|
|
76
72
|
```diff
|
|
77
73
|
- withState(fetchItems())
|
|
78
74
|
+ withState(fetchItems(), { delay: 500 })
|
|
79
75
|
```
|
|
80
76
|
|
|
81
|
-
๐น
|
|
77
|
+
๐น Use case: avoiding flashing a process indicator when the action is likely to complete by the end of a short delay.
|
|
78
|
+
|
|
79
|
+
## Custom rejection handler
|
|
82
80
|
|
|
83
81
|
```diff
|
|
84
82
|
- withState(fetchItems())
|
|
85
83
|
+ withState(fetchItems(), { throws: true }).catch(handleError)
|
|
86
84
|
```
|
|
87
85
|
|
|
88
|
-
๐น
|
|
86
|
+
๐น This option allows the async action to reject explicitly, along with exposing `state.error` that goes by default.
|
|
87
|
+
|
|
88
|
+
## Providing blank initial pending state
|
|
89
89
|
|
|
90
90
|
```diff
|
|
91
91
|
+ import { PendingStateProvider } from "@t8/react-pending";
|
|
@@ -96,7 +96,9 @@ Objective: Track the pending state of the async `fetchItems()` action to tell th
|
|
|
96
96
|
+ </PendingStateProvider>
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
๐น
|
|
99
|
+
๐น `<PendingStateProvider>` creates an isolated instance of initial shared action state. Prime use cases: tests, SSR. It isn't required with client-side rendering, but it can be used to separate action states of larger self-contained portions of a web app.
|
|
100
|
+
|
|
101
|
+
## Providing custom initial pending state
|
|
100
102
|
|
|
101
103
|
```diff
|
|
102
104
|
+ const initialState = {
|
|
@@ -109,4 +111,6 @@ Objective: Track the pending state of the async `fetchItems()` action to tell th
|
|
|
109
111
|
</PendingStateProvider>
|
|
110
112
|
```
|
|
111
113
|
|
|
112
|
-
|
|
114
|
+
๐น While fully optional, this setup allows to override the initial state received from `usePendingState(stateKey)`.
|
|
115
|
+
|
|
116
|
+
๐น With an explicit value or without, the `<PendingStateProvider>`'s nested components will only respond to updates in the particular action states they subscribed to by means of `usePendingState(stateKey)`.
|
package/dist/index.js
CHANGED
|
@@ -12,17 +12,15 @@ function isStore(x) {
|
|
|
12
12
|
// node_modules/@t8/store/src/Store.ts
|
|
13
13
|
var Store = class {
|
|
14
14
|
state;
|
|
15
|
-
callbacks =
|
|
15
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
16
16
|
revision = -1;
|
|
17
17
|
constructor(data) {
|
|
18
18
|
this.state = data;
|
|
19
19
|
}
|
|
20
20
|
onUpdate(callback) {
|
|
21
|
-
this.callbacks.
|
|
21
|
+
this.callbacks.add(callback);
|
|
22
22
|
return () => {
|
|
23
|
-
|
|
24
|
-
if (this.callbacks[i] === callback) this.callbacks.splice(i, 1);
|
|
25
|
-
}
|
|
23
|
+
this.callbacks.delete(callback);
|
|
26
24
|
};
|
|
27
25
|
}
|
|
28
26
|
getState() {
|
package/package.json
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t8/react-pending",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "Concise async action state tracking for React apps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "npx npm-run-all clean compile",
|
|
9
|
-
"clean": "node -e \"require('node:fs').rmSync('dist', {force: true, recursive: true});\"",
|
|
9
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { force: true, recursive: true });\"",
|
|
10
10
|
"compile": "npx esbuild index.ts --bundle --outdir=dist --platform=neutral --external:react",
|
|
11
11
|
"demo": "npx @t8/serve 3000 * tests/async_status -b src/index.tsx",
|
|
12
|
-
"
|
|
13
|
-
"prepublishOnly": "npx npm-run-all build gh-pages",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
14
13
|
"preversion": "npx npm-run-all typecheck shape build test",
|
|
15
14
|
"shape": "npx codeshape",
|
|
16
15
|
"test": "npx playwright test --project=chromium",
|
|
17
16
|
"typecheck": "tsc --noEmit"
|
|
18
17
|
},
|
|
19
18
|
"author": "axtk",
|
|
20
|
-
"license": "
|
|
19
|
+
"license": "MIT",
|
|
21
20
|
"repository": {
|
|
22
21
|
"type": "git",
|
|
23
22
|
"url": "git+https://github.com/t8js/react-pending.git"
|
|
@@ -32,15 +31,15 @@
|
|
|
32
31
|
"react": ">=16.8"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
|
-
"@playwright/test": "^1.
|
|
36
|
-
"@t8/serve": "^0.1.
|
|
34
|
+
"@playwright/test": "^1.56.0",
|
|
35
|
+
"@t8/serve": "^0.1.30",
|
|
37
36
|
"@types/node": "^24.5.2",
|
|
38
37
|
"@types/react": "^19.1.10",
|
|
39
38
|
"@types/react-dom": "^19.1.9",
|
|
40
39
|
"react-dom": "^19.1.1",
|
|
41
|
-
"typescript": "^5.9.
|
|
40
|
+
"typescript": "^5.9.3"
|
|
42
41
|
},
|
|
43
42
|
"dependencies": {
|
|
44
|
-
"@t8/react-store": "^1.0.
|
|
43
|
+
"@t8/react-store": "^1.0.27"
|
|
45
44
|
}
|
|
46
45
|
}
|