1+ import { useContext } from "react" ;
2+ import { PromiseStatus } from "../../models" ;
3+ import * as React from "react" ;
4+
5+ const promiseStatusCache = new WeakMap < Promise < unknown > , PromiseStatus > ( ) ;
6+
7+ /** Type-guard: distinguishes a React Context object from a Promise. */
8+ function isContext < T > ( value : unknown ) : value is React . Context < T > {
9+ return (
10+ value !== null &&
11+ typeof value === "object" &&
12+ "$$typeof" in ( value as object ) &&
13+ typeof ( value as { Provider ?: unknown } ) . Provider !== "undefined" &&
14+ typeof ( value as { Consumer ?: unknown } ) . Consumer !== "undefined"
15+ ) ;
16+ }
17+
18+ /**
19+ * Instruments a promise so its status is always readable synchronously,
20+ * mirroring what React 19's native `use` receives from the scheduler.
21+ */
22+ function trackPromise < T > ( promise : Promise < T > ) : PromiseStatus {
23+ if ( promiseStatusCache . has ( promise ) ) {
24+ return promiseStatusCache . get ( promise ) ! ;
25+ }
26+ const entry : PromiseStatus = {
27+ status : "pending" ,
28+ promise : promise . then (
29+ ( value ) => {
30+ ( entry as unknown as { status : string ; value : unknown } ) . status = "fulfilled" ;
31+ ( entry as unknown as { value : unknown } ) . value = value ;
32+ } ,
33+ ( reason ) => {
34+ ( entry as unknown as { status : string ; reason : unknown } ) . status = "rejected" ;
35+ ( entry as unknown as { reason : unknown } ) . reason = reason ;
36+ }
37+ ) ,
38+ } ;
39+
40+ promiseStatusCache . set ( promise as Promise < unknown > , entry ) ;
41+ return entry ;
42+ }
43+
44+ /**
45+ * **`use`**: Polyfill of React 19's `use` hook for React 16.8 – 18.
46+ *
47+ * Reads the value of a **Promise** or a **React Context**, suspending the
48+ * component (via Suspense) when the promise is still pending, and resuming
49+ * with the resolved value once it settles.
50+ * @see [📖 Documentation](https://react-tools.ndria.dev/hooks/api-dom/use)
51+ *
52+ * @param input - A `Promise<T>` or a `React.Context<T>`.
53+ * @returns The resolved value `T`.
54+ */
55+ function usePolyfill < T > ( input : Promise < T > | React . Context < T > ) : T {
56+ if ( isContext < T > ( input ) ) {
57+ return useContext ( input ) ;
58+ }
59+
60+ const tracked = trackPromise ( input ) ;
61+
62+ switch ( tracked . status ) {
63+ case "fulfilled" :
64+ return ( tracked as { status : "fulfilled" ; value : T } ) . value ;
65+
66+ case "rejected" :
67+ throw ( tracked as { status : "rejected" ; reason : unknown } ) . reason ;
68+
69+ case "pending" :
70+ default :
71+ throw tracked . promise ;
72+ }
73+ }
74+
75+ export const use = ( ( React as any ) . use ?? usePolyfill ) as typeof usePolyfill ;
0 commit comments