Skip to content

Commit 41b085d

Browse files
committed
feat: useParallelPromise hook
1 parent 35fa5d1 commit 41b085d

2 files changed

Lines changed: 86 additions & 11 deletions

File tree

apps/react-tools-demo/src/pages/hooks/api-dom/usePromiseSuspensible/UsePromiseSuspensible.tsx renamed to src/demo/hooks/api-dom/useParallelPromises/UseParallelPromises.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Suspense, useCallback } from "react"
2-
import { ErrorBoundary, usePromiseSuspensible } from "../../../../../../../packages/react-tools-lib/src";
2+
import { ErrorBoundary } from "../../../..";
3+
import { useParallelPromises } from "../../../../hooks/api-dom/useParallelPromises";
34

45
/**
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.
6+
The _Delayed_ component uses _usePromise_ 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 _UsePromise_ component.
67
*/
78
const Delayed = () => {
8-
const [data, invalidate] = usePromiseSuspensible(
9-
async () => {
9+
const {result, invalidate} = useParallelPromises(
10+
[async () => {
1011
return await new Promise<number[]>((res, rej) => {
1112
console.log("called");
1213
setTimeout(() => {
@@ -15,23 +16,20 @@ const Delayed = () => {
1516
: rej("Error throwed by promise");
1617
}, 4000);
1718
});
18-
},
19+
}],
1920
[],
2021
{
21-
cache: 25, //25 seconds
22-
cleanOnError: true,
23-
identifier: "ss",
24-
invalidateManually: true
22+
identifiers: ["ss"],
2523
}
2624
);
2725

2826
return <>
2927
<button onClick={invalidate}>Invalidate</button>
30-
<pre>{JSON.stringify(data)}</pre>
28+
<pre>{JSON.stringify(result[0])}</pre>
3129
</>;
3230
}
3331

34-
export const UsePromiseSuspensible = () => {
32+
export const UseParallelPromise = () => {
3533
const fallback = useCallback<(error: Error, info: React.ErrorInfo, retry: () => void) => React.ReactNode>((_, __, retry) => {
3634
return <button onClick={retry}>Retry</button>
3735
}, []);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { DependencyList } from "react";
2+
3+
/**
4+
* Options accepted by [useParallelPromises](https://react-tools.ndria.dev/hooks/api-dom/useParallelPromises).
5+
*/
6+
export type UseParallelPromisesOptions = {
7+
/**
8+
* Controls how long resolved results are retained
9+
* in the module-level cache:
10+
* - **`"unmount"`** — The cache entry is deleted when the component unmounts, forcing
11+
* a fresh fetch on the next mount.
12+
* - **`number`** — A time-to-live in **seconds**. The cache entry expires after the given
13+
* number of seconds from the moment the promise resolves. Expired entries are deleted on
14+
* the next render and the factory is re-invoked.
15+
* - **`undefined`** *(default)* — The cache entry persists indefinitely across mounts and
16+
* unmounts for the lifetime of the module.
17+
*/
18+
cache?: "unmount" | number;
19+
/**
20+
* An optional array of explicit string keys used to
21+
* identify each factory in the cache, positionally aligned with the `factories` array.
22+
* - When provided, `identifiers[i]` is used as the cache key for `factories[i]`.
23+
* - When omitted, the cache key is derived by serialising the factory function via
24+
* `toString()`, which may be unreliable after minification. Providing stable explicit
25+
* identifiers is strongly recommended in production builds.
26+
*/
27+
identifiers?: string[];
28+
};
29+
30+
/**
31+
* Parameters accepted by [useParallelPromises](https://react-tools.ndria.dev/hooks/api-dom/useParallelPromises).
32+
*
33+
* @template T - A readonly tuple of zero-argument async factory functions. Inferred automatically from the `factories` argument. The return types of each factory determine the corresponding entry in the result tuple.
34+
*/
35+
export type UseParallelPromisesProps<T extends readonly (() => Promise<any>)[]> = {
36+
/**
37+
* A readonly tuple of factory functions, each returning a `Promise`.
38+
* All factories are invoked in parallel on the first render (or after invalidation).
39+
* Each factory must be a zero-argument function: `() => Promise<any>`.
40+
*/
41+
factories: T;
42+
/**
43+
* A React dependency array (same semantics as `useEffect`).
44+
* When any dependency changes, the cached result for the affected factory is considered
45+
* stale and the promise is re-executed on the next render.
46+
*/
47+
deps: DependencyList;
48+
/**
49+
* Optional configuration object.
50+
* See {@link UseParallelPromisesOptions}.
51+
*/
52+
options?: UseParallelPromisesOptions;
53+
};
54+
55+
/**
56+
* Return value of [useParallelPromises](https://react-tools.ndria.dev/hooks/api-dom/useParallelPromises).
57+
*
58+
* @template T - The readonly tuple of factory functions passed to the hook. Used to infer
59+
* the type of each entry in `result`.
60+
*/
61+
export type UseParallelPromisesResult<T extends readonly (() => Promise<any>)[]> = {
62+
/**
63+
* A mutable
64+
* tuple whose length and types mirror the `factories` input. `result[K]` is the awaited
65+
* return type of `factories[K]`. This property is only accessible after all promises have
66+
* fulfilled; if any promise is still pending the component suspends, and if any promise
67+
* rejects the error is re-thrown for the nearest `<ErrorBoundary>` to catch.
68+
*/
69+
result: { -readonly [K in keyof T]: Awaited<ReturnType<T[K]>> };
70+
/**
71+
* A stable callback that clears **all** entries from
72+
* the shared module-level cache and triggers a re-render, causing every factory to be
73+
* re-invoked on the next render cycle. Useful for manual cache invalidation (e.g. after
74+
* a mutation).
75+
*/
76+
invalidate: () => void;
77+
};

0 commit comments

Comments
 (0)