@t8/react-pending 1.0.24 → 1.0.25

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 (3) hide show
  1. package/README.md +20 -6
  2. package/dist/index.js +8 -1
  3. package/package.json +46 -47
package/README.md CHANGED
@@ -4,22 +4,36 @@
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@t8/react-pending?labelColor=345&color=46e)](https://www.npmjs.com/package/@t8/react-pending) ![Lightweight](https://img.shields.io/bundlephobia/minzip/@t8/react-pending?label=minzip&labelColor=345&color=46e) ![CSR ✓](https://img.shields.io/badge/CSR-✓-345?labelColor=345) ![SSR ✓](https://img.shields.io/badge/SSR-✓-345?labelColor=345)
6
6
 
7
- Shared or local pending state tracking with a concise API without rewrites in the app's shared state or async actions' internals.
7
+ **Why?** To manage the async action state, whether local or shared, without tightly coupling it with the app state. Decoupled pending state as described here acts like a lightweight scaffolding on top of the action's and component's successful scenario. It's easy to set up from scratch without rewriting the async actions and affecting the app state, and easy to manage further on since it's barely intertwined with other app's internals.
8
8
 
9
+ <!-- docsgen-show-start --
9
10
  ```diff
10
- + let [state, withState] = usePendingState("fetch-items");
11
+ + import { usePendingState } from "@t8/react-pending";
11
12
 
12
- - fetchItems().then(setItems);
13
- + withState(fetchItems()).then(setItems);
13
+ export let ItemList = () => {
14
+ let [items, setItems] = useState([]);
15
+ + let [state, withState] = usePendingState("fetch-items");
14
16
 
15
- + if (!state.complete) return <p>Loading...</p>;
17
+ useEffect(() => {
18
+ - fetchItems().then(setItems);
19
+ + withState(fetchItems()).then(setItems);
20
+ }, [fetchItems, withState]);
21
+
22
+ + if (!state.complete) return <p>Loading...</p>;
23
+ + if (state.error) return <p>An error occurred</p>;
24
+
25
+ return <ul>{items.map(/* ... */)}</ul>;
26
+ };
16
27
  ```
28
+ -- docsgen-show-end -->
17
29
 
18
30
  Installation: `npm i @t8/react-pending`
19
31
 
20
32
  ## Shared pending state
21
33
 
22
- Objective: Track the pending state of the asynchronous action `fetchItems()` to tell the user whether the UI is busy or encountered an error (preferably without rewriting the action and the app's state management). In our setup, there are two components rendering their content with regard to the current state of `fetchItems()`.
34
+ Objective: Track the pending state of the asynchronous action `fetchItems()` to tell the user whether the UI is busy handling the async action or encountered an error, without rewriting the action and the app's state management.
35
+
36
+ In our setup, there are two components rendering their content with regard to the current state of `fetchItems()`, so the pending state is shared between these components:
23
37
 
24
38
  ```diff
25
39
  + import { usePendingState } from "@t8/react-pending";
package/dist/index.js CHANGED
@@ -9,6 +9,11 @@ function isStore(x) {
9
9
  return x !== null && typeof x === "object" && "onUpdate" in x && typeof x.onUpdate === "function" && "getState" in x && typeof x.getState === "function" && "setState" in x && typeof x.setState === "function";
10
10
  }
11
11
 
12
+ // node_modules/@t8/store/src/isPersistentStore.ts
13
+ function isPersistentStore(x) {
14
+ return isStore(x) && "sync" in x;
15
+ }
16
+
12
17
  // node_modules/@t8/store/src/Store.ts
13
18
  var Store = class {
14
19
  state;
@@ -38,12 +43,14 @@ var Store = class {
38
43
  // node_modules/@t8/react-store/src/useStore.ts
39
44
  import { useEffect, useMemo, useRef, useState } from "react";
40
45
  function useStore(store, shouldUpdate = true) {
41
- if (!isStore(store)) throw new Error("'store' is not an instance of Store");
46
+ if (!isStore(store))
47
+ throw new Error("'store' is not an instance of Store");
42
48
  let [, setRevision] = useState(-1);
43
49
  let state = store.getState();
44
50
  let setState = useMemo(() => store.setState.bind(store), [store]);
45
51
  let initialStoreRevision = useRef(store.revision);
46
52
  useEffect(() => {
53
+ if (isPersistentStore(store)) store.syncOnce();
47
54
  if (!shouldUpdate) return;
48
55
  let unsubscribe = store.onUpdate((nextState, prevState) => {
49
56
  if (typeof shouldUpdate !== "function" || shouldUpdate(nextState, prevState))
package/package.json CHANGED
@@ -1,47 +1,46 @@
1
- {
2
- "name": "@t8/react-pending",
3
- "version": "1.0.24",
4
- "description": "Concise async action state tracking for React apps",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "type": "module",
8
- "scripts": {
9
- "build": "npx npm-run-all clean compile types",
10
- "clean": "node -e \"require('node:fs').rmSync('dist', { force: true, recursive: true });\"",
11
- "compile": "npx esbuild index.ts --bundle --outdir=dist --platform=neutral --external:react",
12
- "demo": "npx @t8/serve 3000 * tests/async_status -b src/index.tsx",
13
- "prepublishOnly": "npm run build",
14
- "preversion": "npx npm-run-all typecheck shape build test",
15
- "shape": "npx codeshape",
16
- "test": "npx playwright test --project=chromium",
17
- "typecheck": "tsc --noEmit",
18
- "types": "tsc -p tsconfig.build.json"
19
- },
20
- "author": "axtk",
21
- "license": "MIT",
22
- "repository": {
23
- "type": "git",
24
- "url": "git+https://github.com/t8js/react-pending.git"
25
- },
26
- "homepage": "https://t8.js.org/react-pending",
27
- "keywords": [
28
- "async actions",
29
- "pending state",
30
- "react"
31
- ],
32
- "peerDependencies": {
33
- "react": ">=16.8"
34
- },
35
- "devDependencies": {
36
- "@playwright/test": "^1.56.0",
37
- "@t8/serve": "^0.1.35",
38
- "@types/node": "^24.5.2",
39
- "@types/react": "^19.1.10",
40
- "@types/react-dom": "^19.1.9",
41
- "react-dom": "^19.1.1",
42
- "typescript": "^5.9.3"
43
- },
44
- "dependencies": {
45
- "@t8/react-store": "^1.0.35"
46
- }
47
- }
1
+ {
2
+ "name": "@t8/react-pending",
3
+ "version": "1.0.25",
4
+ "description": "Concise async action state tracking for React apps",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "scripts": {
9
+ "build": "npx npm-run-all clean compile types",
10
+ "clean": "node -e \"require('node:fs').rmSync('dist', { force: true, recursive: true });\"",
11
+ "compile": "npx esbuild index.ts --bundle --outdir=dist --platform=neutral --external:react",
12
+ "demo": "npx @t8/serve 3000 * tests/async_status -b src/index.tsx",
13
+ "prepublishOnly": "npm run build",
14
+ "preversion": "npx npm-run-all shape build test",
15
+ "shape": "npx codeshape --typecheck",
16
+ "test": "npx playwright test --project=chromium",
17
+ "types": "tsc -p tsconfig.build.json"
18
+ },
19
+ "author": "axtk",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/t8js/react-pending.git"
24
+ },
25
+ "homepage": "https://t8.js.org/react-pending",
26
+ "keywords": [
27
+ "async actions",
28
+ "pending state",
29
+ "react"
30
+ ],
31
+ "peerDependencies": {
32
+ "react": ">=16.8"
33
+ },
34
+ "devDependencies": {
35
+ "@playwright/test": "^1.56.0",
36
+ "@t8/serve": "^0.1.36",
37
+ "@types/node": "^24.10.1",
38
+ "@types/react": "^19.2.7",
39
+ "@types/react-dom": "^19.1.9",
40
+ "react-dom": "^19.2.1",
41
+ "typescript": "^5.9.3"
42
+ },
43
+ "dependencies": {
44
+ "@t8/react-store": "^1.2.3"
45
+ }
46
+ }