@t8/react-pending 1.0.1 → 1.0.3

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 CHANGED
@@ -1,4 +1,4 @@
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)
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/?label=minzip&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
2
 
3
3
  # @t8/react-pending
4
4
 
@@ -0,0 +1,35 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" style="--color-scheme: indigo">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>@t8/react-pending | Concise async action state tracking for React apps</title>
7
+ <link rel="stylesheet" href="https://unpkg.com/ghstage@2.2/dist/css/base.css">
8
+ <link rel="stylesheet" href="https://unpkg.com/ghstage@2.2/dist/css/index.css">
9
+ <link rel="icon" type="image/svg+xml" href="{{site.github.baseurl}}/favicon.svg">
10
+ <link rel="prefetch" href="{{site.github.baseurl}}/start">
11
+ <link rel="prefetch" href="{{site.github.baseurl}}/x/Usage">
12
+ </head>
13
+ <body>
14
+ <div class="layout">
15
+ <main>
16
+ {{content}}
17
+ </main>
18
+ </div>
19
+
20
+ {% if content contains '<pre><code ' %}<link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/styles/base16/material.min.css">
21
+ <link rel="stylesheet" href="https://unpkg.com/ghstage@2.2/dist/css/code.css">
22
+ <script src="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/highlight.min.js"></script>
23
+ <script>hljs.highlightAll()</script>{% elsif content contains '<pre ' %}<link rel="stylesheet" href="https://unpkg.com/ghstage@2.2/dist/css/code.lightbulb.css">
24
+ {% endif %}
25
+ <script>
26
+ (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
27
+ m[i].l=1*new Date();
28
+ for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
29
+ k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
30
+ (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
31
+ ym(103784239, "init", {clickmap: true, trackLinks: true, accurateTrackBounce: true});
32
+ </script>
33
+ <noscript><div><img src="https://mc.yandex.ru/watch/103784239" style="position:absolute;left:-9999px;" alt=""></div></noscript>
34
+ </body>
35
+ </html>
@@ -0,0 +1,58 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" style="--color-scheme: indigo">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>{{page.title | strip_html}} | @t8/react-pending</title>
7
+ <link rel="stylesheet" href="https://unpkg.com/ghstage@2.2/dist/css/base.css">
8
+ <link rel="stylesheet" href="https://unpkg.com/ghstage@2.2/dist/css/section.css">
9
+ <link rel="icon" type="image/svg+xml" href="{{site.github.baseurl}}/favicon.svg">
10
+ {% unless page.next.id == '' %}<link rel="prefetch" href="{{site.github.baseurl}}/x/{{page.next.id}}">{% endunless %}
11
+ {% unless page.prev.id == '' %}<link rel="prefetch" href="{{site.github.baseurl}}/x/{{page.prev.id}}">{% endunless %}
12
+ </head>
13
+ <body>
14
+ <div class="layout">
15
+ <div class="no-nav body">
16
+
17
+ <main>
18
+ <h1><a href="{{site.github.baseurl}}/">@t8/react-pending</a></h1>
19
+ {{content}}
20
+
21
+ <p class="pagenav">
22
+ <span class="prev">
23
+ <span class="icon">←</span>
24
+ {% if page.prev.id == '' %}<a href="{{site.github.baseurl}}/">Intro</a>{% else %}<a href="{{site.github.baseurl}}/x/{{page.prev.id}}">{{page.prev.title}}</a>{% endif %}
25
+ </span>
26
+ <span class="sep">|</span>
27
+ {% if page.next.id == '' %}
28
+ <span class="repo next">
29
+ <a href="https://github.com/t8dash/react-pending" target="_blank">GitHub</a>
30
+ <span class="icon">✦</span>
31
+ </span>
32
+ {% else %}
33
+ <span class="next">
34
+ <a href="{{site.github.baseurl}}/x/{{page.next.id}}">{{page.next.title}}</a>
35
+ <span class="icon">→</span>
36
+ </span>
37
+ {% endif %}
38
+ </p>
39
+ </main>
40
+ </div>
41
+ </div>
42
+
43
+ {% if content contains '<pre><code ' %}<link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/styles/base16/material.min.css">
44
+ <link rel="stylesheet" href="https://unpkg.com/ghstage@2.2/dist/css/code.css">
45
+ <script src="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/highlight.min.js"></script>
46
+ <script>hljs.highlightAll()</script>{% elsif content contains '<pre ' %}<link rel="stylesheet" href="https://unpkg.com/ghstage@2.2/dist/css/code.lightbulb.css">
47
+ {% endif %}
48
+ <script>
49
+ (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
50
+ m[i].l=1*new Date();
51
+ for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
52
+ k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
53
+ (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
54
+ ym(103784239, "init", {clickmap: true, trackLinks: true, accurateTrackBounce: true});
55
+ </script>
56
+ <noscript><div><img src="https://mc.yandex.ru/watch/103784239" style="position:absolute;left:-9999px;" alt=""></div></noscript>
57
+ </body>
58
+ </html>
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="blank" style="--color-scheme: indigo">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width">
6
+ <meta http-equiv="refresh" content="0; URL={{site.github.baseurl}}/x/{{page.start_id}}">
7
+ <title>@t8/react-pending</title>
8
+ <link rel="stylesheet" href="https://unpkg.com/ghstage@2.2/dist/css/base.css">
9
+ <link rel="icon" type="image/svg+xml" href="{{site.github.baseurl}}/favicon.svg">
10
+ <script>window.location.replace("{{site.github.baseurl}}/x/{{page.start_id}}");</script>
11
+ </head>
12
+ <body>
13
+ <div class="layout">
14
+ <h1>@t8/react-pending</h1>
15
+ </div>
16
+
17
+ <script>
18
+ (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
19
+ m[i].l=1*new Date();
20
+ for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
21
+ k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
22
+ (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
23
+ ym(103784239, "init", {clickmap: true, trackLinks: true, accurateTrackBounce: true});
24
+ </script>
25
+ <noscript><div><img src="https://mc.yandex.ru/watch/103784239" style="position:absolute;left:-9999px;" alt=""></div></noscript>
26
+ </body>
27
+ </html>
package/favicon.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><style>.b{fill:indigo;}.c0{fill:oklch(from indigo calc(100*(.78 - l)) 0 0 / .5);}.c1{fill:oklch(from indigo calc(100*(.78 - l)) 0 0 / .3);}.c2{fill:none;}</style><g stroke="none"><path d="M0,15 L50,0 L100,15 L100,78 L50,100 L0,81z" stroke="none" class="b"/><path d="M0,15 L50,30 L100,15 L50,0z" stroke="none" class="c1"/><path d="M0,15 L50,30 L50,100 L0,81z" stroke="none" class="c0"/><path d="M50,30 L100,15 L100,78 L50,100z" stroke="none" class="c2"/></g></svg>
package/index.html ADDED
@@ -0,0 +1,21 @@
1
+ ---
2
+ layout: index
3
+ ---
4
+
5
+ <section class="intro-title">
6
+ <div class="badges">
7
+ <p><a href="https://www.npmjs.com/package/@t8/react-pending"><img src="https://flat.badgen.net/npm/v/@t8/react-pending?labelColor=345&amp;color=46e" alt="npm"></a> <a href="https://bundlephobia.com/package/@t8/react-pending"><img src="https://flat.badgen.net/bundlephobia/minzip/@t8/react-pending/?label=minzip&amp;labelColor=345&amp;color=46e" alt="Lightweight"></a> <img src="https://flat.badgen.net/badge/TypeScript/%E2%9C%93?labelColor=345&amp;color=345" alt="TypeScript ✓"> <img src="https://flat.badgen.net/badge/CSR/%E2%9C%93?labelColor=345&amp;color=345" alt="CSR ✓"> <img src="https://flat.badgen.net/badge/SSR/%E2%9C%93?labelColor=345&amp;color=345" alt="SSR ✓"></p>
8
+ </div>
9
+ <h1>@t8/react-pending</h1>
10
+ <div class="description">
11
+ <p><em>Concise async action state tracking for React apps</em></p>
12
+ <p>No need to rearrange the app's shared state setup and to rewrite the async actions.</p>
13
+ </div>
14
+ <p class="actions">
15
+ <a href="{{site.github.baseurl}}/start" class="primary button">Docs ›››</a>
16
+ <span class="sep"> • </span>
17
+ <a href="https://github.com/t8dash/react-pending" class="button" target="_blank">GitHub</a>
18
+ </p>
19
+ <p class="ref"><a href="https://axtk.github.io/x/t8_react_pending">Backstory</a></p>
20
+ <p class="installation"><code>npm i @t8/react-pending</code></p>
21
+ </section>
package/package.json CHANGED
@@ -1,34 +1,34 @@
1
- {
2
- "name": "@t8/react-pending",
3
- "version": "1.0.1",
4
- "description": "Concise async action state tracking for React apps",
5
- "main": "dist/index.js",
6
- "type": "module",
7
- "scripts": {
8
- "build": "npx npm-run-all clean compile",
9
- "clean": "node -e \"require('node:fs').rmSync('dist', {force: true, recursive: true});\"",
10
- "compile": "npx esbuild index.ts --bundle --outdir=dist --platform=neutral --external:react",
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",
13
- "preversion": "npx npm-run-all shape build",
14
- "shape": "npx codeshape"
15
- },
16
- "author": "axtk",
17
- "license": "ISC",
18
- "repository": {
19
- "type": "git",
20
- "url": "git+https://github.com/t8dev/react-pending.git"
21
- },
22
- "keywords": [
23
- "async actions",
24
- "pending state",
25
- "react"
26
- ],
27
- "peerDependencies": {
28
- "@t8/react-store": "^1.0.0",
29
- "react": ">=16.8"
30
- },
31
- "devDependencies": {
32
- "@types/react": "^19.1.10"
33
- }
34
- }
1
+ {
2
+ "name": "@t8/react-pending",
3
+ "version": "1.0.3",
4
+ "description": "Concise async action state tracking for React apps",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "npx npm-run-all clean compile",
9
+ "clean": "node -e \"require('node:fs').rmSync('dist', {force: true, recursive: true});\"",
10
+ "compile": "npx esbuild index.ts --bundle --outdir=dist --platform=neutral --external:react",
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",
13
+ "preversion": "npx npm-run-all shape build",
14
+ "shape": "npx codeshape"
15
+ },
16
+ "author": "axtk",
17
+ "license": "ISC",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/t8dash/react-pending.git"
21
+ },
22
+ "keywords": [
23
+ "async actions",
24
+ "pending state",
25
+ "react"
26
+ ],
27
+ "peerDependencies": {
28
+ "@t8/react-store": "^1.0.2",
29
+ "react": ">=16.8"
30
+ },
31
+ "devDependencies": {
32
+ "@types/react": "^19.1.10"
33
+ }
34
+ }
package/start.html ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: start
3
+ start_id: "Usage"
4
+ ---
package/x/Usage.html ADDED
@@ -0,0 +1,90 @@
1
+ ---
2
+ layout: section
3
+ id: "Usage"
4
+ title: "Usage"
5
+ prev:
6
+ id: ""
7
+ title: ""
8
+ next:
9
+ id: ""
10
+ title: ""
11
+ ---
12
+
13
+ <h2 id="usage">Usage</h2>
14
+ <p>Objective: Track the pending state of the async <code>fetchItems()</code> 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).</p>
15
+ <pre><code class="language-diff">+ import {usePendingState} from '@t8/react-pending';
16
+
17
+ const ItemList = () =&gt; {
18
+ const [items, setItems] = useState([]);
19
+ // the custom string key parameter tags the action's state so
20
+ // that another component can access this state by the same tag
21
+ + const [state, withState] = usePendingState('fetch-items');
22
+
23
+ useEffect(() =&gt; {
24
+ // wrapping fetchItems() to track the async action's state
25
+ - fetchItems().then(setItems);
26
+ + withState(fetchItems()).then(setItems);
27
+ }, [fetchItems, withState]);
28
+
29
+ + if (!state.complete)
30
+ + return &lt;p&gt;Loading...&lt;/p&gt;;
31
+
32
+ + if (state.error)
33
+ + return &lt;p&gt;An error occurred&lt;/p&gt;;
34
+
35
+ return &lt;ul&gt;{items.map(/* ... */)}&lt;/ul&gt;;
36
+ };
37
+
38
+ const Status = () =&gt; {
39
+ // reading the 'fetch-items' state updated in ItemList
40
+ + const [state] = usePendingState('fetch-items');
41
+
42
+ if (!state.initialized)
43
+ return 'Initial';
44
+
45
+ if (!state.complete)
46
+ return 'Busy';
47
+
48
+ if (state.error)
49
+ return 'Error';
50
+
51
+ return 'OK';
52
+ };
53
+ </code></pre>
54
+ <p><a href="https://codesandbox.io/p/sandbox/9rrsg9?file=%2Fsrc%2FItemList.js" target="_blank">Live demo</a></p>
55
+ <p>🔹 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 <code>usePendingState()</code> hook.</p>
56
+ <pre><code class="language-diff">- const [state, withState] = usePendingState('fetch-items');
57
+ + const [state, withState] = usePendingState();
58
+ </code></pre>
59
+ <p>🔹 In the example above, the action's value (the <code>items</code> 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.</p>
60
+ <p>🔹 Silently tracking the action's pending state, e.g. with background or optimistic updates (preventing <code>state.complete</code> from switching to <code>false</code> in the pending state):</p>
61
+ <pre><code class="language-diff">- withState(fetchItems())
62
+ + withState(fetchItems(), {silent: true})
63
+ </code></pre>
64
+ <p>🔹 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):</p>
65
+ <pre><code class="language-diff">- withState(fetchItems())
66
+ + withState(fetchItems(), {delay: 500})
67
+ </code></pre>
68
+ <p>🔹 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 <code>state.error</code> that goes by default:</p>
69
+ <pre><code class="language-diff">- withState(fetchItems())
70
+ + withState(fetchItems(), {throws: true}).catch(handleError)
71
+ </code></pre>
72
+ <p>🔹 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):</p>
73
+ <pre><code class="language-diff">+ import {PendingStateProvider} from '@t8/react-pending';
74
+
75
+ - &lt;App/&gt;
76
+ + &lt;PendingStateProvider&gt;
77
+ + &lt;App/&gt;
78
+ + &lt;/PendingStateProvider&gt;
79
+ </code></pre>
80
+ <p>🔹 Setting a custom initial action state (which is fully optional):</p>
81
+ <pre><code class="language-diff">+ const initialState = {
82
+ + 'fetch-items': { initialized: true, complete: true },
83
+ + };
84
+
85
+ - &lt;PendingStateProvider&gt;
86
+ + &lt;PendingStateProvider value={initialState}&gt;
87
+ &lt;App/&gt;
88
+ &lt;/PendingStateProvider&gt;
89
+ </code></pre>
90
+ <p>With an explicit value or without, the <code>&lt;PendingStateProvider&gt;</code>'s nested components will only respond to updates in the particular action states they subscribed to by means of <code>usePendingState('action-key')</code>.</p>