@t8/react-pending 0.1.0 → 1.0.1

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.
Files changed (2) hide show
  1. package/README.md +112 -0
  2. package/package.json +3 -2
package/README.md CHANGED
@@ -0,0 +1,112 @@
1
+ [![npm](https://flat.badgen.net/npm/v/@t8/react-pending?labelColor=345&color=46e)](https://www.npmjs.com/package/@t8/react-pending) [![Lightweight](https://flat.badgen.net/bundlephobia/minzip/@t8/react-pending/?labelColor=345&color=46e)](https://bundlephobia.com/package/@t8/react-pending) ![TypeScript ✓](https://flat.badgen.net/badge/TypeScript/✓?labelColor=345&color=345) ![CSR ✓](https://flat.badgen.net/badge/CSR/✓?labelColor=345&color=345) ![SSR ✓](https://flat.badgen.net/badge/SSR/✓?labelColor=345&color=345)
2
+
3
+ # @t8/react-pending
4
+
5
+ *Concise async action state tracking for React apps*
6
+
7
+ No need to rearrange the app's shared state setup and to rewrite the async actions.
8
+
9
+ Installation: `npm i @t8/react-pending`
10
+
11
+ ## Usage
12
+
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
+
15
+ ```diff
16
+ + import {usePendingState} from '@t8/react-pending';
17
+
18
+ const ItemList = () => {
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
+ + const [state, withState] = usePendingState('fetch-items');
23
+
24
+ useEffect(() => {
25
+ // wrapping fetchItems() to track the async action's state
26
+ - fetchItems().then(setItems);
27
+ + withState(fetchItems()).then(setItems);
28
+ }, [fetchItems, withState]);
29
+
30
+ + if (!state.complete)
31
+ + return <p>Loading...</p>;
32
+
33
+ + if (state.error)
34
+ + return <p>An error occurred</p>;
35
+
36
+ return <ul>{items.map(/* ... */)}</ul>;
37
+ };
38
+
39
+ const Status = () => {
40
+ // reading the 'fetch-items' state updated in ItemList
41
+ + const [state] = usePendingState('fetch-items');
42
+
43
+ if (!state.initialized)
44
+ return 'Initial';
45
+
46
+ if (!state.complete)
47
+ return 'Busy';
48
+
49
+ if (state.error)
50
+ return 'Error';
51
+
52
+ return 'OK';
53
+ };
54
+ ```
55
+
56
+ [Live demo](https://codesandbox.io/p/sandbox/9rrsg9?file=%2Fsrc%2FItemList.js)
57
+
58
+ 🔹 If the action's state is only used within a single component, it can be used locally by omitting the custom string key parameter of the `usePendingState()` hook.
59
+
60
+ ```diff
61
+ - const [state, withState] = usePendingState('fetch-items');
62
+ + const [state, withState] = usePendingState();
63
+ ```
64
+
65
+ 🔹 In the example above, the action's value (the `items` array) is stored in the component's local state, but it can certainly live in the app's shared state of the developer's choice instead.
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):
68
+
69
+ ```diff
70
+ - withState(fetchItems())
71
+ + withState(fetchItems(), {silent: true})
72
+ ```
73
+
74
+ 🔹 Revealing the action's pending state after a delay (e.g. to avoid flashing a process indicator when the action is likely to complete by the end of the delay):
75
+
76
+ ```diff
77
+ - withState(fetchItems())
78
+ + withState(fetchItems(), {delay: 500})
79
+ ```
80
+
81
+ 🔹 Allowing the action's Promise value to reject explicitly (e.g. in order to provide the action with a custom rejection handler) along with exposing `state.error` that goes by default:
82
+
83
+ ```diff
84
+ - withState(fetchItems())
85
+ + withState(fetchItems(), {throws: true}).catch(handleError)
86
+ ```
87
+
88
+ 🔹 Providing an isolated instance of initial shared action state, e.g. for tests or SSR (it can be unnecessary for client-side rendering where the default context value is sufficient, but it can also be used to separate action states of larger self-contained portions of a web app):
89
+
90
+ ```diff
91
+ + import {PendingStateProvider} from '@t8/react-pending';
92
+
93
+ - <App/>
94
+ + <PendingStateProvider>
95
+ + <App/>
96
+ + </PendingStateProvider>
97
+ ```
98
+
99
+ 🔹 Setting a custom initial action state (which is fully optional):
100
+
101
+ ```diff
102
+ + const initialState = {
103
+ + 'fetch-items': { initialized: true, complete: true },
104
+ + };
105
+
106
+ - <PendingStateProvider>
107
+ + <PendingStateProvider value={initialState}>
108
+ <App/>
109
+ </PendingStateProvider>
110
+ ```
111
+
112
+ 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('action-key')`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t8/react-pending",
3
- "version": "0.1.0",
3
+ "version": "1.0.1",
4
4
  "description": "Concise async action state tracking for React apps",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -8,7 +8,8 @@
8
8
  "build": "npx npm-run-all clean compile",
9
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
- "prepublishOnly": "npm run build",
11
+ "gh-pages": "npx ghstage --color-scheme=indigo --ymid=103784239 --backstory=https://axtk.github.io/x/t8_react_pending",
12
+ "prepublishOnly": "npx npm-run-all build gh-pages",
12
13
  "preversion": "npx npm-run-all shape build",
13
14
  "shape": "npx codeshape"
14
15
  },