Skip to content

Commit 3cbd1a4

Browse files
committed
[FIX] usePromiseSuspensible invalidateManually option
1 parent 836f615 commit 3cbd1a4

5 files changed

Lines changed: 41 additions & 13 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,7 @@ usePopover({ mode, onBeforeToggle, onToggle }: UsePopoverProps): UsePopoverResul
855855
856856
Hook to resolve promise with Suspense support. The component that uses it, it need to be wrapped with Suspense component. This hook can be used in conditional blocks. [See demo](https://react-tools.ndria.dev/#/hooks/api-dom/usePromiseSuspensible)
857857
```tsx
858-
usePromiseSuspensible<T>(promise: ()=>Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string } = {}): Awaited<ReturnType<typeof promise>>
858+
usePromiseSuspensible<T>(promise: () => Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string, invalidateManually?: boolean } = {}): Awaited<ReturnType<typeof promise>> | [Awaited<ReturnType<typeof promise>>, () => void]
859859
```
860860
861861
### usePublishSubscribe

apps/react-tools-demo/src/markdown/usePromiseSuspensible.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Hook to resolve promise with Suspense support. The component that uses it, it ne
55

66
```tsx
77
const Delayed = () => {
8-
const data = usePromiseSuspensible(
8+
const [data, invalidate] = usePromiseSuspensible(
99
async () => {
1010
return await new Promise<number[]>((res, rej) => {
1111
console.log("called");
@@ -20,11 +20,13 @@ const Delayed = () => {
2020
{
2121
cache: 25, //25 seconds
2222
cleanOnError: true,
23-
identifier: "ss"
23+
identifier: "ss",
24+
invalidateManually: true
2425
}
2526
);
2627

2728
return <>
29+
<button onClick={invalidate}>Invalidate</button>
2830
<pre>{JSON.stringify(data)}</pre>
2931
</>;
3032
}
@@ -48,7 +50,7 @@ export const UsePromiseSuspensible = () => {
4850
## API
4951

5052
```tsx
51-
usePromiseSuspensible<T>(promise: ()=>Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string } = {}): Awaited<ReturnType<typeof promise>>
53+
usePromiseSuspensible<T>(promise: () => Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string, invalidateManually?: boolean } = {}): Awaited<ReturnType<typeof promise>> | [Awaited<ReturnType<typeof promise>>, () => void]
5254
```
5355

5456

@@ -64,6 +66,8 @@ optional options.
6466
value can be "unmount", to clean promise cached at component unmounting, or it can be the duration in __seconds__ of cached promise.
6567
> - __options.cleanOnError=undefined?__: _boolean_
6668
if true, when there is an error, remove promise from cache with a delay of 20 millisecond (due to multiple renders of react strict mode).
69+
> - __options.invalidateManually=undefined?__: _boolean_
70+
if true, returns data and a function to invalidate data and revaluates promise.
6771
> - __options.identifier=undefined?__: _string_
6872
a string to identify _promise_. If it isn't present, a serialization of _promise_ will be used.
6973
>
@@ -73,5 +77,9 @@ a string to identify _promise_. If it isn't present, a serialization of _promise
7377
> ### Returns
7478
>
7579
> __result__: resolve promise value.
76-
> - _Awaited<ReturnType<T>>_
80+
> - __Union of__:
81+
> - _Awaited<ReturnType<T>>_
82+
> - __Array__:
83+
> - _Awaited<ReturnType<T>>_
84+
> - _()=>void_
7785
>

apps/react-tools-demo/src/pages/hooks/api-dom/usePromiseSuspensible/UsePromiseSuspensible.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { Suspense, useCallback } from "react"
22
import { ErrorBoundary, usePromiseSuspensible } from "../../../../../../../packages/react-tools-lib/src";
33

44
/**
5-
The _Delayed_ component uses _usePromiseSuspensible_ hook to call a promise that resolves with an array of number or reject: if promise has been resolved, array number is rendered, otherwise an alert is invocked. Delayed component is returned from _UsePromiseSuspensible_ component.
5+
The _Delayed_ component uses _usePromiseSuspensible_ hook to call a promise that resolves with an array of number or reject: if promise has been resolved, array number is rendered with a button to invalidate result, otherwise an alert is invocked. Delayed component is returned from _UsePromiseSuspensible_ component.
66
*/
77
const Delayed = () => {
8-
const data = usePromiseSuspensible(
8+
const [data, invalidate] = usePromiseSuspensible(
99
async () => {
1010
return await new Promise<number[]>((res, rej) => {
1111
console.log("called");
@@ -20,11 +20,13 @@ const Delayed = () => {
2020
{
2121
cache: 25, //25 seconds
2222
cleanOnError: true,
23-
identifier: "ss"
23+
identifier: "ss",
24+
invalidateManually: true
2425
}
2526
);
2627

2728
return <>
29+
<button onClick={invalidate}>Invalidate</button>
2830
<pre>{JSON.stringify(data)}</pre>
2931
</>;
3032
}

packages/react-tools-lib/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,7 @@ usePopover({ mode, onBeforeToggle, onToggle }: UsePopoverProps): UsePopoverResul
855855
856856
Hook to resolve promise with Suspense support. The component that uses it, it need to be wrapped with Suspense component. This hook can be used in conditional blocks. [See demo](https://react-tools.ndria.dev/#/hooks/api-dom/usePromiseSuspensible)
857857
```tsx
858-
usePromiseSuspensible<T>(promise: ()=>Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string } = {}): Awaited<ReturnType<typeof promise>>
858+
usePromiseSuspensible<T>(promise: () => Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string, invalidateManually?: boolean } = {}): Awaited<ReturnType<typeof promise>> | [Awaited<ReturnType<typeof promise>>, () => void]
859859
```
860860
861861
### usePublishSubscribe

packages/react-tools-lib/src/hooks/api-dom/usePromiseSuspensible.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DependencyList } from "react";
1+
import { DependencyList, useReducer } from "react";
22
import { useEffectOnce } from "../lifecycle";
33
import { isDeepEqual } from "../../utils";
44

@@ -11,11 +11,17 @@ const promiseCache: { deps: DependencyList, promise: Promise<void>, error?: unkn
1111
* @param {{ clearCacheOnUnmount?: "unmount"|number, cleanOnError?: boolean }} [options] - optional options.
1212
* @param {"unmount"|number} [options.cache=undefined] - value can be "unmount", to clean promise cached at component unmounting, or it can be the duration in __seconds__ of cached promise.
1313
* @param {boolean} [options.cleanOnError=undefined] - if true, when there is an error, remove promise from cache with a delay of 20 millisecond (due to multiple renders of react strict mode).
14+
* @param {boolean} [options.invalidateManually=undefined] - if true, returns resolved promise value and a function to invalidate and revaluate promise.
1415
* @param {string} [options.identifier=undefined] - a string to identify _promise_. If it isn't present, a serialization of _promise_ will be used.
15-
* @returns {Awaited<ReturnType<T>>} result - resolve promise value.
16+
* @returns {Awaited<ReturnType<T>> | [Awaited<ReturnType<T>>, ()=>void]} result - resolve promise value.
1617
*/
17-
export const usePromiseSuspensible = <T>(promise: ()=>Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string } = {}): Awaited<ReturnType<typeof promise>> => {
18+
function usePromiseSuspensible<T>(promise: () => Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string, invalidateManually?: undefined }): Awaited<ReturnType<typeof promise>>;
19+
function usePromiseSuspensible<T>(promise: () => Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string, invalidateManually?: false }): Awaited<ReturnType<typeof promise>>;
20+
function usePromiseSuspensible<T>(promise: () => Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string, invalidateManually?: true }): [Awaited<ReturnType<typeof promise>>, ()=>void];
21+
function usePromiseSuspensible<T>(promise: () => Promise<T>, deps: DependencyList, options: { cache?: "unmount" | number, cleanOnError?: boolean, identifier?: string, invalidateManually?: boolean } = {}): Awaited<ReturnType<typeof promise>> | [Awaited<ReturnType<typeof promise>>, () => void] {
1822
let index = -1;
23+
const [, reRender] = useReducer(t => t + 0.00000000001, 0);
24+
1925
useEffectOnce(() => () => {
2026
if (options.cache === "unmount") {
2127
index !== -1 && promiseCache.splice(index, 1);
@@ -39,6 +45,16 @@ export const usePromiseSuspensible = <T>(promise: ()=>Promise<T>, deps: Dependen
3945
throw promiseCache[ind].error;
4046
}
4147
if ("response" in promiseCache[ind]) {
48+
if (options.invalidateManually) {
49+
return [
50+
promiseCache[ind].response,
51+
() => {
52+
index !== -1 && promiseCache.splice(index, 1);
53+
index = -1;
54+
reRender();
55+
}
56+
] as [Awaited<ReturnType<typeof promise>>, () => void]
57+
}
4258
return promiseCache[ind].response as Awaited<ReturnType<typeof promise>>;
4359
}
4460
throw promiseCache[ind].promise;
@@ -60,4 +76,6 @@ export const usePromiseSuspensible = <T>(promise: ()=>Promise<T>, deps: Dependen
6076

6177
index = promiseCache.push(cached) - 1;
6278
throw cached.promise;
63-
}
79+
}
80+
81+
export { usePromiseSuspensible };

0 commit comments

Comments
 (0)