import { useReducer, useCallback, useRef } from "react"; type Status = "idle" | "pending" | "resolved" | "rejected"; type Action = | { type: "start" } | { type: "resolve"; payload: T } | { type: "reject"; payload: Error }; type State = { status: Status; result: T | null; error: Error | null; }; function reducer(state: State, action: Action): State { switch (action.type) { case "start": return { ...state, status: "pending" }; case "resolve": return { status: "resolved", result: action.payload, error: null }; case "reject": return { status: "rejected", result: null, error: action.payload }; default: throw new Error("Unhandled action type"); } } export function useSuspendable(): [ T | null, Error | null, Status, (promise: Promise) => Promise, Promise | null, ] { const [state, dispatch] = useReducer(reducer, { status: "idle", result: null, error: null, }); const pendingPromise = useRef | null>(null); const suspend = useCallback((promise: Promise) => { pendingPromise.current = promise; dispatch({ type: "start" }); return promise .then((data) => { dispatch({ type: "resolve", payload: data }); }) .catch((error) => { dispatch({ type: "reject", payload: error as Error }); }); }, []); return [ state.result as T | null, state.error, state.status, suspend, pendingPromise.current, ]; }