Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import { DependencyList, useEffect, useReducer } from "react";
- type PromiseFunction<T> = (signal: AbortSignal) => Promise<T>;
- type Result<T> =
- | { status: "pending" }
- | { status: "fulfilled"; value: T }
- | { status: "rejected"; reason: unknown };
- type State<T> =
- | { status: "uninitialized" }
- | { status: "pending"; id: number; abortController: AbortController }
- | { status: "fulfilled"; value: T }
- | { status: "rejected"; reason: unknown };
- type Action<T> =
- | { type: "initialize"; id: number; abortController: AbortController }
- | { type: "resolve"; id: number; value: T }
- | { type: "reject"; id: number; reason: unknown }
- | { type: "clean" };
- function reducer<T>(state: State<T>, action: Action<T>): State<T> {
- switch (action.type) {
- case "initialize":
- switch (state.status) {
- case "uninitialized":
- return {
- status: "pending",
- id: action.id,
- abortController: action.abortController,
- };
- }
- break;
- case "resolve":
- switch (state.status) {
- case "uninitialized":
- return state;
- case "pending":
- if (action.id < state.id) {
- return state;
- }
- if (action.id == state.id) {
- return { status: "fulfilled", value: action.value };
- }
- break;
- }
- break;
- case "reject":
- switch (state.status) {
- case "uninitialized":
- return state;
- case "pending":
- if (action.id < state.id) {
- return state;
- }
- if (action.id == state.id) {
- return { status: "rejected", reason: action.reason };
- }
- break;
- }
- break;
- case "clean":
- switch (state.status) {
- case "pending":
- state.abortController.abort();
- return { status: "uninitialized" };
- case "fulfilled":
- case "rejected":
- return { status: "uninitialized" };
- }
- break;
- }
- console.error("Unexpected state", state, action);
- return state;
- }
- let nextId = 1;
- export default function usePromise<T>(
- promiseFunction: PromiseFunction<T>,
- deps: DependencyList,
- ): Result<T> {
- const [state, dispatch] = useReducer(reducer<T>, {
- status: "uninitialized",
- });
- useEffect(() => {
- const id = nextId++;
- const abortController = new AbortController();
- dispatch({ type: "initialize", id, abortController });
- promiseFunction(abortController.signal).then(
- (value) => dispatch({ type: "resolve", id, value }),
- (reason) => dispatch({ type: "reject", id, reason }),
- );
- return () => dispatch({ type: "clean" });
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, deps);
- switch (state.status) {
- case "uninitialized":
- case "pending":
- return { status: "pending" };
- case "fulfilled":
- return { status: "fulfilled", value: state.value };
- case "rejected":
- return { status: "rejected", reason: state.reason };
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment