Guest User

Untitled

a guest
Feb 1st, 2023
137
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { DependencyList, useEffect, useReducer } from "react";
  2.  
  3. type PromiseFunction<T> = (signal: AbortSignal) => Promise<T>;
  4.  
  5. type Result<T> =
  6.   | { status: "pending" }
  7.   | { status: "fulfilled"; value: T }
  8.   | { status: "rejected"; reason: unknown };
  9.  
  10. type State<T> =
  11.   | { status: "uninitialized" }
  12.   | { status: "pending"; id: number; abortController: AbortController }
  13.   | { status: "fulfilled"; value: T }
  14.   | { status: "rejected"; reason: unknown };
  15.  
  16. type Action<T> =
  17.   | { type: "initialize"; id: number; abortController: AbortController }
  18.   | { type: "resolve"; id: number; value: T }
  19.   | { type: "reject"; id: number; reason: unknown }
  20.   | { type: "clean" };
  21.  
  22. function reducer<T>(state: State<T>, action: Action<T>): State<T> {
  23.   switch (action.type) {
  24.     case "initialize":
  25.       switch (state.status) {
  26.         case "uninitialized":
  27.           return {
  28.             status: "pending",
  29.             id: action.id,
  30.             abortController: action.abortController,
  31.           };
  32.       }
  33.       break;
  34.  
  35.     case "resolve":
  36.       switch (state.status) {
  37.         case "uninitialized":
  38.           return state;
  39.         case "pending":
  40.           if (action.id < state.id) {
  41.             return state;
  42.           }
  43.           if (action.id == state.id) {
  44.             return { status: "fulfilled", value: action.value };
  45.           }
  46.           break;
  47.       }
  48.       break;
  49.  
  50.     case "reject":
  51.       switch (state.status) {
  52.         case "uninitialized":
  53.           return state;
  54.         case "pending":
  55.           if (action.id < state.id) {
  56.             return state;
  57.           }
  58.           if (action.id == state.id) {
  59.             return { status: "rejected", reason: action.reason };
  60.           }
  61.           break;
  62.       }
  63.       break;
  64.  
  65.     case "clean":
  66.       switch (state.status) {
  67.         case "pending":
  68.           state.abortController.abort();
  69.           return { status: "uninitialized" };
  70.         case "fulfilled":
  71.         case "rejected":
  72.           return { status: "uninitialized" };
  73.       }
  74.       break;
  75.   }
  76.  
  77.   console.error("Unexpected state", state, action);
  78.   return state;
  79. }
  80.  
  81. let nextId = 1;
  82.  
  83. export default function usePromise<T>(
  84.   promiseFunction: PromiseFunction<T>,
  85.   deps: DependencyList,
  86. ): Result<T> {
  87.   const [state, dispatch] = useReducer(reducer<T>, {
  88.     status: "uninitialized",
  89.   });
  90.  
  91.   useEffect(() => {
  92.     const id = nextId++;
  93.     const abortController = new AbortController();
  94.  
  95.     dispatch({ type: "initialize", id, abortController });
  96.  
  97.     promiseFunction(abortController.signal).then(
  98.       (value) => dispatch({ type: "resolve", id, value }),
  99.       (reason) => dispatch({ type: "reject", id, reason }),
  100.     );
  101.  
  102.     return () => dispatch({ type: "clean" });
  103.     // eslint-disable-next-line react-hooks/exhaustive-deps
  104.   }, deps);
  105.  
  106.   switch (state.status) {
  107.     case "uninitialized":
  108.     case "pending":
  109.       return { status: "pending" };
  110.  
  111.     case "fulfilled":
  112.       return { status: "fulfilled", value: state.value };
  113.  
  114.     case "rejected":
  115.       return { status: "rejected", reason: state.reason };
  116.   }
  117. }
  118.  
Advertisement
Add Comment
Please, Sign In to add comment