@t8/react-pending 1.1.1 → 1.2.0
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 +11 -11
- package/dist/index.cjs +14 -13
- package/dist/index.d.ts +12 -11
- package/dist/index.mjs +14 -13
- package/package.json +2 -2
- package/src/PendingState.ts +3 -3
- package/src/usePendingState.ts +18 -17
package/README.md
CHANGED
|
@@ -12,14 +12,14 @@ Self-contained async action state management for React apps
|
|
|
12
12
|
|
|
13
13
|
export let ItemList = () => {
|
|
14
14
|
let [items, setItems] = useState([]);
|
|
15
|
-
+ let {
|
|
15
|
+
+ let { initial, pending, error, track } = usePendingState("fetch-items");
|
|
16
16
|
|
|
17
17
|
useEffect(() => {
|
|
18
18
|
- fetchItems().then(setItems);
|
|
19
19
|
+ track(fetchItems()).then(setItems);
|
|
20
20
|
}, [fetchItems, track]);
|
|
21
21
|
|
|
22
|
-
+ if (
|
|
22
|
+
+ if (initial || pending) return <p>Loading...</p>;
|
|
23
23
|
+ if (error) return <p>An error occurred</p>;
|
|
24
24
|
|
|
25
25
|
return <ul>{items.map(/* ... */)}</ul>;
|
|
@@ -40,14 +40,14 @@ In our setup, there are two components rendering their content with regard to th
|
|
|
40
40
|
|
|
41
41
|
export let ItemList = () => {
|
|
42
42
|
let [items, setItems] = useState([]);
|
|
43
|
-
+ let {
|
|
43
|
+
+ let { initial, pending, error, track } = usePendingState("fetch-items");
|
|
44
44
|
|
|
45
45
|
useEffect(() => {
|
|
46
46
|
- fetchItems().then(setItems);
|
|
47
47
|
+ track(fetchItems()).then(setItems);
|
|
48
48
|
}, [fetchItems, track]);
|
|
49
49
|
|
|
50
|
-
+ if (
|
|
50
|
+
+ if (initial || pending) return <p>Loading...</p>;
|
|
51
51
|
+ if (error) return <p>An error occurred</p>;
|
|
52
52
|
|
|
53
53
|
return <ul>{items.map(/* ... */)}</ul>;
|
|
@@ -58,10 +58,10 @@ In our setup, there are two components rendering their content with regard to th
|
|
|
58
58
|
+ import { usePendingState } from "@t8/react-pending";
|
|
59
59
|
|
|
60
60
|
export let Status = () => {
|
|
61
|
-
+ let {
|
|
61
|
+
+ let { initial, pending, error } = usePendingState("fetch-items");
|
|
62
62
|
|
|
63
|
-
if (
|
|
64
|
-
if (
|
|
63
|
+
if (initial) return null;
|
|
64
|
+
if (pending) return <>Busy</>;
|
|
65
65
|
if (error) return <>Error</>;
|
|
66
66
|
|
|
67
67
|
return <>OK</>;
|
|
@@ -79,8 +79,8 @@ In our setup, there are two components rendering their content with regard to th
|
|
|
79
79
|
Omit the custom string key parameter of `usePendingState()` to scope the pending state locally within a single component:
|
|
80
80
|
|
|
81
81
|
```diff
|
|
82
|
-
- let {
|
|
83
|
-
+ let {
|
|
82
|
+
- let { initial, pending, error } = usePendingState("fetch-items"); // shared
|
|
83
|
+
+ let { initial, pending, error } = usePendingState(); // local
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
## Silent tracking of background actions and optimistic updates
|
|
@@ -90,7 +90,7 @@ Omit the custom string key parameter of `usePendingState()` to scope the pending
|
|
|
90
90
|
+ track(fetchItems(), { silent: true })
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
⬥ This option prevents `
|
|
93
|
+
⬥ This option prevents the `pending` property from switching to `false` in the pending state.
|
|
94
94
|
|
|
95
95
|
## Delayed pending state
|
|
96
96
|
|
|
@@ -127,7 +127,7 @@ Omit the custom string key parameter of `usePendingState()` to scope the pending
|
|
|
127
127
|
|
|
128
128
|
```diff
|
|
129
129
|
+ let initialState = {
|
|
130
|
-
+ "fetch-items": {
|
|
130
|
+
+ "fetch-items": { initial: false, pending: true },
|
|
131
131
|
+ };
|
|
132
132
|
|
|
133
133
|
- <PendingStateProvider>
|
package/dist/index.cjs
CHANGED
|
@@ -18,10 +18,10 @@ const PendingStateProvider = ({ value, children }) => {
|
|
|
18
18
|
});
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
function createState(
|
|
21
|
+
function createState(initial = true, pending = false, error) {
|
|
22
22
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
initial,
|
|
24
|
+
pending,
|
|
25
25
|
error,
|
|
26
26
|
time: Date.now()
|
|
27
27
|
};
|
|
@@ -29,15 +29,16 @@ function createState(initialized = false, complete = false, error) {
|
|
|
29
29
|
/**
|
|
30
30
|
* Returns an instance of an action's state and the functions to update it.
|
|
31
31
|
*
|
|
32
|
-
* @param store -
|
|
32
|
+
* @param store - An optional unique string key or a store. Providing a
|
|
33
33
|
* key or a shared store allows to share the state across multiple
|
|
34
|
-
* components.
|
|
34
|
+
* components. If omitted, the pending state stays locally scoped to the
|
|
35
|
+
* component where the hook is used.
|
|
35
36
|
*
|
|
36
|
-
* @returns `{
|
|
37
|
-
* - `
|
|
37
|
+
* @returns `{ initial, pending, error, track, update }`, where
|
|
38
|
+
* - `initial`, `pending`, `error` reflect the current action's state;
|
|
38
39
|
* - `track(action, options?)` tracks the `actions`'s state;
|
|
39
40
|
* - `update(nextState | ((state) => nextState))` can be used to replace
|
|
40
|
-
* the current
|
|
41
|
+
* the current pending state directly with an another value.
|
|
41
42
|
*/
|
|
42
43
|
function usePendingState(store) {
|
|
43
44
|
let storeMap = (0, react.useContext)(PendingStateContext);
|
|
@@ -68,12 +69,12 @@ function usePendingState(store) {
|
|
|
68
69
|
let delay = options?.delay;
|
|
69
70
|
if (delay === void 0) setState((prevState) => ({
|
|
70
71
|
...prevState,
|
|
71
|
-
...createState(
|
|
72
|
+
...createState(false, true)
|
|
72
73
|
}));
|
|
73
74
|
else delayedPending = setTimeout(() => {
|
|
74
75
|
setState((prevState) => ({
|
|
75
76
|
...prevState,
|
|
76
|
-
...createState(
|
|
77
|
+
...createState(false, true)
|
|
77
78
|
}));
|
|
78
79
|
delayedPending = null;
|
|
79
80
|
}, delay);
|
|
@@ -82,21 +83,21 @@ function usePendingState(store) {
|
|
|
82
83
|
if (delayedPending !== null) clearTimeout(delayedPending);
|
|
83
84
|
setState((prevState) => ({
|
|
84
85
|
...prevState,
|
|
85
|
-
...createState(
|
|
86
|
+
...createState(false, false)
|
|
86
87
|
}));
|
|
87
88
|
return resolvedValue;
|
|
88
89
|
}).catch((error) => {
|
|
89
90
|
if (delayedPending !== null) clearTimeout(delayedPending);
|
|
90
91
|
setState((prevState) => ({
|
|
91
92
|
...prevState,
|
|
92
|
-
...createState(
|
|
93
|
+
...createState(false, false, error)
|
|
93
94
|
}));
|
|
94
95
|
if (options?.throws) throw error;
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
98
|
setState((prevState) => ({
|
|
98
99
|
...prevState,
|
|
99
|
-
...createState(
|
|
100
|
+
...createState(false, false)
|
|
100
101
|
}));
|
|
101
102
|
return value;
|
|
102
103
|
}, [setState]);
|
package/dist/index.d.ts
CHANGED
|
@@ -4,10 +4,10 @@ import { SetStoreValue, Store } from "@t8/react-store";
|
|
|
4
4
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
5
|
|
|
6
6
|
type PendingState = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
time?: number | undefined;
|
|
7
|
+
initial?: boolean | undefined;
|
|
8
|
+
pending?: boolean | undefined;
|
|
10
9
|
error?: unknown;
|
|
10
|
+
time?: number | undefined;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
declare const PendingStateContext: react0.Context<Map<string, Store<PendingState>>>;
|
|
@@ -26,12 +26,12 @@ type TrackOptions = {
|
|
|
26
26
|
* Whether to track the action state silently (e.g. with a background
|
|
27
27
|
* action or an optimistic update).
|
|
28
28
|
*
|
|
29
|
-
* When set to `true`, the state's `
|
|
30
|
-
* to `
|
|
29
|
+
* When set to `true`, the state's `pending` property doesn't switch
|
|
30
|
+
* to `true` in the pending state.
|
|
31
31
|
*/
|
|
32
32
|
silent?: boolean;
|
|
33
33
|
/**
|
|
34
|
-
* Delays switching the action state's `
|
|
34
|
+
* Delays switching the action state's `pending` property to `true`
|
|
35
35
|
* in the pending state by the given number of milliseconds.
|
|
36
36
|
*
|
|
37
37
|
* Use case: to avoid flashing a process indicator if the action is
|
|
@@ -47,15 +47,16 @@ type TrackOptions = {
|
|
|
47
47
|
/**
|
|
48
48
|
* Returns an instance of an action's state and the functions to update it.
|
|
49
49
|
*
|
|
50
|
-
* @param store -
|
|
50
|
+
* @param store - An optional unique string key or a store. Providing a
|
|
51
51
|
* key or a shared store allows to share the state across multiple
|
|
52
|
-
* components.
|
|
52
|
+
* components. If omitted, the pending state stays locally scoped to the
|
|
53
|
+
* component where the hook is used.
|
|
53
54
|
*
|
|
54
|
-
* @returns `{
|
|
55
|
-
* - `
|
|
55
|
+
* @returns `{ initial, pending, error, track, update }`, where
|
|
56
|
+
* - `initial`, `pending`, `error` reflect the current action's state;
|
|
56
57
|
* - `track(action, options?)` tracks the `actions`'s state;
|
|
57
58
|
* - `update(nextState | ((state) => nextState))` can be used to replace
|
|
58
|
-
* the current
|
|
59
|
+
* the current pending state directly with an another value.
|
|
59
60
|
*/
|
|
60
61
|
declare function usePendingState(store?: string | Store<PendingState> | null): PendingState & {
|
|
61
62
|
track: <T>(value: T) => T;
|
package/dist/index.mjs
CHANGED
|
@@ -18,10 +18,10 @@ const PendingStateProvider = ({ value, children }) => {
|
|
|
18
18
|
});
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
function createState(
|
|
21
|
+
function createState(initial = true, pending = false, error) {
|
|
22
22
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
initial,
|
|
24
|
+
pending,
|
|
25
25
|
error,
|
|
26
26
|
time: Date.now()
|
|
27
27
|
};
|
|
@@ -29,15 +29,16 @@ function createState(initialized = false, complete = false, error) {
|
|
|
29
29
|
/**
|
|
30
30
|
* Returns an instance of an action's state and the functions to update it.
|
|
31
31
|
*
|
|
32
|
-
* @param store -
|
|
32
|
+
* @param store - An optional unique string key or a store. Providing a
|
|
33
33
|
* key or a shared store allows to share the state across multiple
|
|
34
|
-
* components.
|
|
34
|
+
* components. If omitted, the pending state stays locally scoped to the
|
|
35
|
+
* component where the hook is used.
|
|
35
36
|
*
|
|
36
|
-
* @returns `{
|
|
37
|
-
* - `
|
|
37
|
+
* @returns `{ initial, pending, error, track, update }`, where
|
|
38
|
+
* - `initial`, `pending`, `error` reflect the current action's state;
|
|
38
39
|
* - `track(action, options?)` tracks the `actions`'s state;
|
|
39
40
|
* - `update(nextState | ((state) => nextState))` can be used to replace
|
|
40
|
-
* the current
|
|
41
|
+
* the current pending state directly with an another value.
|
|
41
42
|
*/
|
|
42
43
|
function usePendingState(store) {
|
|
43
44
|
let storeMap = useContext(PendingStateContext);
|
|
@@ -68,12 +69,12 @@ function usePendingState(store) {
|
|
|
68
69
|
let delay = options?.delay;
|
|
69
70
|
if (delay === void 0) setState((prevState) => ({
|
|
70
71
|
...prevState,
|
|
71
|
-
...createState(
|
|
72
|
+
...createState(false, true)
|
|
72
73
|
}));
|
|
73
74
|
else delayedPending = setTimeout(() => {
|
|
74
75
|
setState((prevState) => ({
|
|
75
76
|
...prevState,
|
|
76
|
-
...createState(
|
|
77
|
+
...createState(false, true)
|
|
77
78
|
}));
|
|
78
79
|
delayedPending = null;
|
|
79
80
|
}, delay);
|
|
@@ -82,21 +83,21 @@ function usePendingState(store) {
|
|
|
82
83
|
if (delayedPending !== null) clearTimeout(delayedPending);
|
|
83
84
|
setState((prevState) => ({
|
|
84
85
|
...prevState,
|
|
85
|
-
...createState(
|
|
86
|
+
...createState(false, false)
|
|
86
87
|
}));
|
|
87
88
|
return resolvedValue;
|
|
88
89
|
}).catch((error) => {
|
|
89
90
|
if (delayedPending !== null) clearTimeout(delayedPending);
|
|
90
91
|
setState((prevState) => ({
|
|
91
92
|
...prevState,
|
|
92
|
-
...createState(
|
|
93
|
+
...createState(false, false, error)
|
|
93
94
|
}));
|
|
94
95
|
if (options?.throws) throw error;
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
98
|
setState((prevState) => ({
|
|
98
99
|
...prevState,
|
|
99
|
-
...createState(
|
|
100
|
+
...createState(false, false)
|
|
100
101
|
}));
|
|
101
102
|
return value;
|
|
102
103
|
}, [setState]);
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t8/react-pending",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Self-contained async action state management for React apps",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|
|
10
|
-
"demo": "npx @t8/serve tests/
|
|
10
|
+
"demo": "npx @t8/serve tests/async_state --spa -b src/index.tsx",
|
|
11
11
|
"preversion": "npx npm-run-all shape test",
|
|
12
12
|
"shape": "npx codeshape",
|
|
13
13
|
"test": "npx playwright test --project=chromium"
|
package/src/PendingState.ts
CHANGED
package/src/usePendingState.ts
CHANGED
|
@@ -4,13 +4,13 @@ import type { PendingState } from "./PendingState.ts";
|
|
|
4
4
|
import { PendingStateContext } from "./PendingStateContext.ts";
|
|
5
5
|
|
|
6
6
|
function createState(
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
initial = true,
|
|
8
|
+
pending = false,
|
|
9
9
|
error?: unknown,
|
|
10
10
|
): PendingState {
|
|
11
11
|
return {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
initial,
|
|
13
|
+
pending,
|
|
14
14
|
error,
|
|
15
15
|
time: Date.now(),
|
|
16
16
|
};
|
|
@@ -21,12 +21,12 @@ export type TrackOptions = {
|
|
|
21
21
|
* Whether to track the action state silently (e.g. with a background
|
|
22
22
|
* action or an optimistic update).
|
|
23
23
|
*
|
|
24
|
-
* When set to `true`, the state's `
|
|
25
|
-
* to `
|
|
24
|
+
* When set to `true`, the state's `pending` property doesn't switch
|
|
25
|
+
* to `true` in the pending state.
|
|
26
26
|
*/
|
|
27
27
|
silent?: boolean;
|
|
28
28
|
/**
|
|
29
|
-
* Delays switching the action state's `
|
|
29
|
+
* Delays switching the action state's `pending` property to `true`
|
|
30
30
|
* in the pending state by the given number of milliseconds.
|
|
31
31
|
*
|
|
32
32
|
* Use case: to avoid flashing a process indicator if the action is
|
|
@@ -43,15 +43,16 @@ export type TrackOptions = {
|
|
|
43
43
|
/**
|
|
44
44
|
* Returns an instance of an action's state and the functions to update it.
|
|
45
45
|
*
|
|
46
|
-
* @param store -
|
|
46
|
+
* @param store - An optional unique string key or a store. Providing a
|
|
47
47
|
* key or a shared store allows to share the state across multiple
|
|
48
|
-
* components.
|
|
48
|
+
* components. If omitted, the pending state stays locally scoped to the
|
|
49
|
+
* component where the hook is used.
|
|
49
50
|
*
|
|
50
|
-
* @returns `{
|
|
51
|
-
* - `
|
|
51
|
+
* @returns `{ initial, pending, error, track, update }`, where
|
|
52
|
+
* - `initial`, `pending`, `error` reflect the current action's state;
|
|
52
53
|
* - `track(action, options?)` tracks the `actions`'s state;
|
|
53
54
|
* - `update(nextState | ((state) => nextState))` can be used to replace
|
|
54
|
-
* the current
|
|
55
|
+
* the current pending state directly with an another value.
|
|
55
56
|
*/
|
|
56
57
|
export function usePendingState(
|
|
57
58
|
store?: string | Store<PendingState> | null,
|
|
@@ -97,13 +98,13 @@ export function usePendingState(
|
|
|
97
98
|
if (delay === undefined)
|
|
98
99
|
setState((prevState) => ({
|
|
99
100
|
...prevState,
|
|
100
|
-
...createState(
|
|
101
|
+
...createState(false, true),
|
|
101
102
|
}));
|
|
102
103
|
else
|
|
103
104
|
delayedPending = setTimeout(() => {
|
|
104
105
|
setState((prevState) => ({
|
|
105
106
|
...prevState,
|
|
106
|
-
...createState(
|
|
107
|
+
...createState(false, true),
|
|
107
108
|
}));
|
|
108
109
|
|
|
109
110
|
delayedPending = null;
|
|
@@ -116,7 +117,7 @@ export function usePendingState(
|
|
|
116
117
|
|
|
117
118
|
setState((prevState) => ({
|
|
118
119
|
...prevState,
|
|
119
|
-
...createState(
|
|
120
|
+
...createState(false, false),
|
|
120
121
|
}));
|
|
121
122
|
|
|
122
123
|
return resolvedValue;
|
|
@@ -126,7 +127,7 @@ export function usePendingState(
|
|
|
126
127
|
|
|
127
128
|
setState((prevState) => ({
|
|
128
129
|
...prevState,
|
|
129
|
-
...createState(
|
|
130
|
+
...createState(false, false, error),
|
|
130
131
|
}));
|
|
131
132
|
|
|
132
133
|
if (options?.throws) throw error;
|
|
@@ -135,7 +136,7 @@ export function usePendingState(
|
|
|
135
136
|
|
|
136
137
|
setState((prevState) => ({
|
|
137
138
|
...prevState,
|
|
138
|
-
...createState(
|
|
139
|
+
...createState(false, false),
|
|
139
140
|
}));
|
|
140
141
|
|
|
141
142
|
return value;
|