Skip to content

Commit 0b4a67c

Browse files
committed
fix: rename usePromiseSuspensible to usePromise
1 parent 61d5b4c commit 0b4a67c

2 files changed

Lines changed: 128 additions & 0 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Suspense, useCallback } from "react"
2+
import { ErrorBoundary, usePromise } from "../../../..";
3+
4+
/**
5+
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.
6+
*/
7+
const Delayed = () => {
8+
const [data, invalidate] = usePromise(
9+
async () => {
10+
return await new Promise<number[]>((res, rej) => {
11+
console.log("called");
12+
setTimeout(() => {
13+
Math.random() > 0.5
14+
? res([1, 2, 3, 4, 5])
15+
: rej("Error throwed by promise");
16+
}, 4000);
17+
});
18+
},
19+
[],
20+
{
21+
cache: 25, //25 seconds
22+
cleanOnError: true,
23+
identifier: "ss",
24+
invalidateManually: true
25+
}
26+
);
27+
28+
return <>
29+
<button onClick={invalidate}>Invalidate</button>
30+
<pre>{JSON.stringify(data)}</pre>
31+
</>;
32+
}
33+
34+
export const UsePromise = () => {
35+
const fallback = useCallback<(error: Error, info: React.ErrorInfo, retry: () => void) => React.ReactNode>((_, __, retry) => {
36+
return <button onClick={retry}>Retry</button>
37+
}, []);
38+
39+
return <ErrorBoundary fallback={fallback}>
40+
<Suspense fallback="loading...">
41+
<Delayed />
42+
</Suspense>
43+
</ErrorBoundary>
44+
}

src/models/usePromise.model.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { DependencyList } from "react";
2+
3+
/**
4+
* Parameters accepted by [usePromise](https://react-tools.ndria.dev/hooks/api-dom/usePromise).
5+
*
6+
* @template T - The resolved value type of the promise returned by `promise`.
7+
*/
8+
export type UsePromiseProps<T> = {
9+
/**
10+
* A zero-argument factory function that returns the `Promise` to resolve.
11+
* Invoked on the first render and again whenever `deps` changes or the cache
12+
* is invalidated. The component suspends while the promise is pending, and
13+
* re-renders with the resolved value once it fulfills.
14+
*/
15+
promise: () => Promise<T>;
16+
/**
17+
* A React dependency array (same semantics as `useEffect`). When any dependency
18+
* changes, the cached result is considered stale and `promise` is re-invoked on
19+
* the next render.
20+
*/
21+
deps: DependencyList;
22+
/**
23+
* Optional configuration that controls caching, error handling, and invalidation behaviour.
24+
*/
25+
options?: UsePromiseOptions;
26+
};
27+
28+
/**
29+
* Options accepted by {@link usePromise}.
30+
*/
31+
export type UsePromiseOptions = {
32+
/**
33+
* Controls how long the resolved result is retained in the module-level cache:
34+
* - **`"unmount"`** — The cache entry is deleted when the component unmounts,
35+
* forcing a fresh fetch on the next mount.
36+
* - **`number`** — A time-to-live in **seconds**. The entry expires that many
37+
* seconds after the promise resolves and is re-fetched on the next render.
38+
* - **`undefined`** *(default)* — The entry persists indefinitely for the
39+
* lifetime of the module.
40+
*/
41+
cache?: "unmount" | number;
42+
/**
43+
* When `true`, a rejected promise automatically removes its cache entry after a
44+
* short delay (~20 ms), allowing the factory to be retried on the next render
45+
* instead of permanently surfacing the error to the nearest `<ErrorBoundary>`.
46+
* Defaults to `false`.
47+
*/
48+
cleanOnError?: boolean;
49+
/**
50+
* An explicit string key used to identify this promise in the module-level cache.
51+
* - When provided, this value is used as the cache key instead of the serialised
52+
* `promise.toString()` representation, which may be unreliable after minification.
53+
* - Providing a stable identifier is strongly recommended in production builds.
54+
*/
55+
identifier?: string;
56+
/**
57+
* Controls whether the hook exposes a manual invalidation callback:
58+
* - **`true`** — The hook returns `[T, () => void]`: the resolved value paired with
59+
* an `invalidate` function that clears the cache entry and triggers a re-render,
60+
* causing the factory to be re-invoked.
61+
* - **`false` | `undefined`** *(default)* — The hook returns `T` directly, with no
62+
* invalidation handle exposed.
63+
*/
64+
invalidateManually?: boolean;
65+
};
66+
67+
/**
68+
* Return value of [usePromise](https://react-tools.ndria.dev/hooks/api-dom/usePromise) when `invalidateManually` is `true`.
69+
*
70+
* @template T - The resolved value type of the promise.
71+
*/
72+
export type UsePromiseResultWithInvalidate<T> = [
73+
/**
74+
* The resolved value of the promise. Only accessible after the promise fulfills;
75+
* the component suspends while pending and throws for `<ErrorBoundary>` on rejection.
76+
*/
77+
T,
78+
/**
79+
* A stable callback that clears the cache entry for this promise and triggers a
80+
* re-render, causing the factory to be re-invoked on the next render cycle.
81+
* Useful for manual cache invalidation after a mutation or user action.
82+
*/
83+
() => void
84+
];

0 commit comments

Comments
 (0)