@t8/react-pending 1.0.33 → 1.1.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.
package/README.md CHANGED
@@ -12,15 +12,15 @@ Self-contained async action state management for React apps
12
12
 
13
13
  export let ItemList = () => {
14
14
  let [items, setItems] = useState([]);
15
- + let [state, withState] = usePendingState("fetch-items");
15
+ + let { complete, error, track } = usePendingState("fetch-items");
16
16
 
17
17
  useEffect(() => {
18
18
  - fetchItems().then(setItems);
19
- + withState(fetchItems()).then(setItems);
20
- }, [fetchItems, withState]);
19
+ + track(fetchItems()).then(setItems);
20
+ }, [fetchItems, track]);
21
21
 
22
- + if (!state.complete) return <p>Loading...</p>;
23
- + if (state.error) return <p>An error occurred</p>;
22
+ + if (!complete) return <p>Loading...</p>;
23
+ + if (error) return <p>An error occurred</p>;
24
24
 
25
25
  return <ul>{items.map(/* ... */)}</ul>;
26
26
  };
@@ -40,15 +40,15 @@ 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 [state, withState] = usePendingState("fetch-items");
43
+ + let { complete, error, track } = usePendingState("fetch-items");
44
44
 
45
45
  useEffect(() => {
46
46
  - fetchItems().then(setItems);
47
- + withState(fetchItems()).then(setItems);
48
- }, [fetchItems, withState]);
47
+ + track(fetchItems()).then(setItems);
48
+ }, [fetchItems, track]);
49
49
 
50
- + if (!state.complete) return <p>Loading...</p>;
51
- + if (state.error) return <p>An error occurred</p>;
50
+ + if (!complete) return <p>Loading...</p>;
51
+ + if (error) return <p>An error occurred</p>;
52
52
 
53
53
  return <ul>{items.map(/* ... */)}</ul>;
54
54
  };
@@ -58,11 +58,11 @@ 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 [state] = usePendingState("fetch-items");
61
+ + let { initialized, complete, error } = usePendingState("fetch-items");
62
62
 
63
- if (!state.initialized) return null;
64
- if (!state.complete) return <>Busy</>;
65
- if (state.error) return <>Error</>;
63
+ if (!initialized) return null;
64
+ if (!complete) return <>Busy</>;
65
+ if (error) return <>Error</>;
66
66
 
67
67
  return <>OK</>;
68
68
  };
@@ -79,24 +79,24 @@ 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 [state, withState] = usePendingState("fetch-items"); // shared
83
- + let [state, withState] = usePendingState(); // local
82
+ - let { complete } = usePendingState("fetch-items"); // shared
83
+ + let { complete } = usePendingState(); // local
84
84
  ```
85
85
 
86
86
  ## Silent tracking of background actions and optimistic updates
87
87
 
88
88
  ```diff
89
- - withState(fetchItems())
90
- + withState(fetchItems(), { silent: true })
89
+ - track(fetchItems())
90
+ + track(fetchItems(), { silent: true })
91
91
  ```
92
92
 
93
- ⬥ This option prevents `state.complete` from switching to `false` in the pending state.
93
+ ⬥ This option prevents `complete` from switching to `false` in the pending state.
94
94
 
95
95
  ## Delayed pending state
96
96
 
97
97
  ```diff
98
- - withState(fetchItems())
99
- + withState(fetchItems(), { delay: 500 }) // in milliseconds
98
+ - track(fetchItems())
99
+ + track(fetchItems(), { delay: 500 }) // in milliseconds
100
100
  ```
101
101
 
102
102
  ⬥ Use case: avoiding flashing a process indicator when the action is likely to complete by the end of a short delay.
@@ -104,11 +104,11 @@ Omit the custom string key parameter of `usePendingState()` to scope the pending
104
104
  ## Custom rejection handler
105
105
 
106
106
  ```diff
107
- - withState(fetchItems())
108
- + withState(fetchItems(), { throws: true }).catch(handleError)
107
+ - track(fetchItems())
108
+ + track(fetchItems(), { throws: true }).catch(handleError)
109
109
  ```
110
110
 
111
- ⬥ This option allows the async action to reject explicitly, along with exposing `state.error` that goes by default.
111
+ ⬥ This option allows the async action to reject explicitly, along with exposing `error` returned from `usePendingState()` that goes by default.
112
112
 
113
113
  ## Providing blank initial pending&nbsp;state
114
114
 
package/dist/index.cjs CHANGED
@@ -33,12 +33,11 @@ function createState(initialized = false, complete = false, error) {
33
33
  * key or a shared store allows to share the state across multiple
34
34
  * components.
35
35
  *
36
- * @returns `[state, withState, setState]`, where
37
- * - `state` is the current value of the action's state;
38
- * - `withState(action, options?)` reads and tracks the `actions`'s state
39
- * which is exposed as `state` listed above;
40
- * - `setState(update)` can be used to replace the current `state` value
41
- * directly with an another state value.
36
+ * @returns `{ initialized, complete, error, track, update }`, where
37
+ * - `initialized`, `complete`, `error` reflect the current action's state;
38
+ * - `track(action, options?)` tracks the `actions`'s state;
39
+ * - `update(nextState | ((state) => nextState))` can be used to replace
40
+ * the current `state` value directly with an another state value.
42
41
  */
43
42
  function usePendingState(store) {
44
43
  let storeMap = (0, react.useContext)(PendingStateContext);
@@ -62,49 +61,54 @@ function usePendingState(store) {
62
61
  storeMap,
63
62
  storeItemInited
64
63
  ]));
65
- return [
66
- state,
67
- (0, react.useCallback)((value, options) => {
68
- if (value instanceof Promise) {
69
- let delayedPending = null;
70
- if (!options?.silent) {
71
- let delay = options?.delay;
72
- if (delay === void 0) setState((prevState) => ({
73
- ...prevState,
74
- ...createState(true, false)
75
- }));
76
- else delayedPending = setTimeout(() => {
77
- setState((prevState) => ({
78
- ...prevState,
79
- ...createState(true, false)
80
- }));
81
- delayedPending = null;
82
- }, delay);
83
- }
84
- return value.then((resolvedValue) => {
85
- if (delayedPending !== null) clearTimeout(delayedPending);
86
- setState((prevState) => ({
87
- ...prevState,
88
- ...createState(true, true)
89
- }));
90
- return resolvedValue;
91
- }).catch((error) => {
92
- if (delayedPending !== null) clearTimeout(delayedPending);
64
+ let track = (0, react.useCallback)((value, options) => {
65
+ if (value instanceof Promise) {
66
+ let delayedPending = null;
67
+ if (!options?.silent) {
68
+ let delay = options?.delay;
69
+ if (delay === void 0) setState((prevState) => ({
70
+ ...prevState,
71
+ ...createState(true, false)
72
+ }));
73
+ else delayedPending = setTimeout(() => {
93
74
  setState((prevState) => ({
94
75
  ...prevState,
95
- ...createState(true, true, error)
76
+ ...createState(true, false)
96
77
  }));
97
- if (options?.throws) throw error;
98
- });
78
+ delayedPending = null;
79
+ }, delay);
99
80
  }
100
- setState((prevState) => ({
101
- ...prevState,
102
- ...createState(true, true)
103
- }));
104
- return value;
105
- }, [setState]),
81
+ return value.then((resolvedValue) => {
82
+ if (delayedPending !== null) clearTimeout(delayedPending);
83
+ setState((prevState) => ({
84
+ ...prevState,
85
+ ...createState(true, true)
86
+ }));
87
+ return resolvedValue;
88
+ }).catch((error) => {
89
+ if (delayedPending !== null) clearTimeout(delayedPending);
90
+ setState((prevState) => ({
91
+ ...prevState,
92
+ ...createState(true, true, error)
93
+ }));
94
+ if (options?.throws) throw error;
95
+ });
96
+ }
97
+ setState((prevState) => ({
98
+ ...prevState,
99
+ ...createState(true, true)
100
+ }));
101
+ return value;
102
+ }, [setState]);
103
+ return (0, react.useMemo)(() => ({
104
+ ...state,
105
+ track,
106
+ update: setState
107
+ }), [
108
+ state,
109
+ track,
106
110
  setState
107
- ];
111
+ ]);
108
112
  }
109
113
 
110
114
  exports.PendingStateContext = PendingStateContext;
package/dist/index.d.ts CHANGED
@@ -21,7 +21,7 @@ declare const PendingStateProvider: ({
21
21
  children
22
22
  }: PendingStateProviderProps) => react_jsx_runtime0.JSX.Element;
23
23
 
24
- type WithStateOptions = {
24
+ type TrackOptions = {
25
25
  /**
26
26
  * Whether to track the action state silently (e.g. with a background
27
27
  * action or an optimistic update).
@@ -51,13 +51,15 @@ type WithStateOptions = {
51
51
  * key or a shared store allows to share the state across multiple
52
52
  * components.
53
53
  *
54
- * @returns `[state, withState, setState]`, where
55
- * - `state` is the current value of the action's state;
56
- * - `withState(action, options?)` reads and tracks the `actions`'s state
57
- * which is exposed as `state` listed above;
58
- * - `setState(update)` can be used to replace the current `state` value
59
- * directly with an another state value.
54
+ * @returns `{ initialized, complete, error, track, update }`, where
55
+ * - `initialized`, `complete`, `error` reflect the current action's state;
56
+ * - `track(action, options?)` tracks the `actions`'s state;
57
+ * - `update(nextState | ((state) => nextState))` can be used to replace
58
+ * the current `state` value directly with an another state value.
60
59
  */
61
- declare function usePendingState(store?: string | Store<PendingState> | null): [PendingState, <T>(value: T) => T, SetStoreValue<PendingState>];
60
+ declare function usePendingState(store?: string | Store<PendingState> | null): PendingState & {
61
+ track: <T>(value: T) => T;
62
+ update: SetStoreValue<PendingState>;
63
+ };
62
64
 
63
- export { PendingState, PendingStateContext, PendingStateProvider, PendingStateProviderProps, WithStateOptions, usePendingState };
65
+ export { PendingState, PendingStateContext, PendingStateProvider, PendingStateProviderProps, TrackOptions, usePendingState };
package/dist/index.mjs CHANGED
@@ -33,12 +33,11 @@ function createState(initialized = false, complete = false, error) {
33
33
  * key or a shared store allows to share the state across multiple
34
34
  * components.
35
35
  *
36
- * @returns `[state, withState, setState]`, where
37
- * - `state` is the current value of the action's state;
38
- * - `withState(action, options?)` reads and tracks the `actions`'s state
39
- * which is exposed as `state` listed above;
40
- * - `setState(update)` can be used to replace the current `state` value
41
- * directly with an another state value.
36
+ * @returns `{ initialized, complete, error, track, update }`, where
37
+ * - `initialized`, `complete`, `error` reflect the current action's state;
38
+ * - `track(action, options?)` tracks the `actions`'s state;
39
+ * - `update(nextState | ((state) => nextState))` can be used to replace
40
+ * the current `state` value directly with an another state value.
42
41
  */
43
42
  function usePendingState(store) {
44
43
  let storeMap = useContext(PendingStateContext);
@@ -62,49 +61,54 @@ function usePendingState(store) {
62
61
  storeMap,
63
62
  storeItemInited
64
63
  ]));
65
- return [
66
- state,
67
- useCallback((value, options) => {
68
- if (value instanceof Promise) {
69
- let delayedPending = null;
70
- if (!options?.silent) {
71
- let delay = options?.delay;
72
- if (delay === void 0) setState((prevState) => ({
73
- ...prevState,
74
- ...createState(true, false)
75
- }));
76
- else delayedPending = setTimeout(() => {
77
- setState((prevState) => ({
78
- ...prevState,
79
- ...createState(true, false)
80
- }));
81
- delayedPending = null;
82
- }, delay);
83
- }
84
- return value.then((resolvedValue) => {
85
- if (delayedPending !== null) clearTimeout(delayedPending);
86
- setState((prevState) => ({
87
- ...prevState,
88
- ...createState(true, true)
89
- }));
90
- return resolvedValue;
91
- }).catch((error) => {
92
- if (delayedPending !== null) clearTimeout(delayedPending);
64
+ let track = useCallback((value, options) => {
65
+ if (value instanceof Promise) {
66
+ let delayedPending = null;
67
+ if (!options?.silent) {
68
+ let delay = options?.delay;
69
+ if (delay === void 0) setState((prevState) => ({
70
+ ...prevState,
71
+ ...createState(true, false)
72
+ }));
73
+ else delayedPending = setTimeout(() => {
93
74
  setState((prevState) => ({
94
75
  ...prevState,
95
- ...createState(true, true, error)
76
+ ...createState(true, false)
96
77
  }));
97
- if (options?.throws) throw error;
98
- });
78
+ delayedPending = null;
79
+ }, delay);
99
80
  }
100
- setState((prevState) => ({
101
- ...prevState,
102
- ...createState(true, true)
103
- }));
104
- return value;
105
- }, [setState]),
81
+ return value.then((resolvedValue) => {
82
+ if (delayedPending !== null) clearTimeout(delayedPending);
83
+ setState((prevState) => ({
84
+ ...prevState,
85
+ ...createState(true, true)
86
+ }));
87
+ return resolvedValue;
88
+ }).catch((error) => {
89
+ if (delayedPending !== null) clearTimeout(delayedPending);
90
+ setState((prevState) => ({
91
+ ...prevState,
92
+ ...createState(true, true, error)
93
+ }));
94
+ if (options?.throws) throw error;
95
+ });
96
+ }
97
+ setState((prevState) => ({
98
+ ...prevState,
99
+ ...createState(true, true)
100
+ }));
101
+ return value;
102
+ }, [setState]);
103
+ return useMemo(() => ({
104
+ ...state,
105
+ track,
106
+ update: setState
107
+ }), [
108
+ state,
109
+ track,
106
110
  setState
107
- ];
111
+ ]);
108
112
  }
109
113
 
110
114
  export { PendingStateContext, PendingStateProvider, usePendingState };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t8/react-pending",
3
- "version": "1.0.33",
3
+ "version": "1.1.1",
4
4
  "description": "Self-contained async action state management for React apps",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -16,7 +16,7 @@ function createState(
16
16
  };
17
17
  }
18
18
 
19
- export type WithStateOptions = {
19
+ export type TrackOptions = {
20
20
  /**
21
21
  * Whether to track the action state silently (e.g. with a background
22
22
  * action or an optimistic update).
@@ -47,16 +47,18 @@ export type WithStateOptions = {
47
47
  * key or a shared store allows to share the state across multiple
48
48
  * components.
49
49
  *
50
- * @returns `[state, withState, setState]`, where
51
- * - `state` is the current value of the action's state;
52
- * - `withState(action, options?)` reads and tracks the `actions`'s state
53
- * which is exposed as `state` listed above;
54
- * - `setState(update)` can be used to replace the current `state` value
55
- * directly with an another state value.
50
+ * @returns `{ initialized, complete, error, track, update }`, where
51
+ * - `initialized`, `complete`, `error` reflect the current action's state;
52
+ * - `track(action, options?)` tracks the `actions`'s state;
53
+ * - `update(nextState | ((state) => nextState))` can be used to replace
54
+ * the current `state` value directly with an another state value.
56
55
  */
57
56
  export function usePendingState(
58
57
  store?: string | Store<PendingState> | null,
59
- ): [PendingState, <T>(value: T) => T, SetStoreValue<PendingState>] {
58
+ ): PendingState & {
59
+ track: <T>(value: T) => T;
60
+ update: SetStoreValue<PendingState>;
61
+ } {
60
62
  let storeMap = useContext(PendingStateContext);
61
63
  let storeRef = useRef<Store<PendingState> | null>(null);
62
64
  let [storeItemInited, setStoreItemInited] = useState(false);
@@ -84,8 +86,8 @@ export function usePendingState(
84
86
 
85
87
  let [state, setState] = useStore(resolvedStore);
86
88
 
87
- let withState = useCallback(
88
- <T>(value: T, options?: WithStateOptions): T => {
89
+ let track = useCallback(
90
+ <T>(value: T, options?: TrackOptions): T => {
89
91
  if (value instanceof Promise) {
90
92
  let delayedPending: ReturnType<typeof setTimeout> | null = null;
91
93
 
@@ -141,5 +143,12 @@ export function usePendingState(
141
143
  [setState],
142
144
  );
143
145
 
144
- return [state, withState, setState];
146
+ return useMemo(
147
+ () => ({
148
+ ...state,
149
+ track,
150
+ update: setState,
151
+ }),
152
+ [state, track, setState],
153
+ );
145
154
  }