Last active
August 25, 2023 04:43
-
-
Save zhenghaohe/2ebddf315a92933bb2a7961c9998799b to your computer and use it in GitHub Desktop.
useAsync.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Status = "pending" | "resolved" | "rejected"; | |
type State<R, E = unknown> = { | |
status: Status; | |
data: null | R; | |
error: null | E; | |
}; | |
type Action<R, E = unknown> = | |
| { type: "pending" } | |
| { type: "resolved"; data: R } | |
| { type: "rejected"; error: E }; | |
function useSafeDispatch<R, E = unknown>( | |
dispatch: React.Dispatch<Action<R, E>> | |
): React.Dispatch<Action<R, E>> { | |
const mountRef = useRef(false); | |
useLayoutEffect(() => { | |
mountRef.current = true; | |
return () => { | |
mountRef.current = false; | |
}; | |
}, []); | |
return useCallback( | |
(...args) => { | |
if (mountRef.current) dispatch(...args); | |
}, | |
[dispatch] | |
); | |
} | |
function createReducer<R, E>() { | |
return function reducer( | |
state: State<R, E>, | |
action: Action<R, E> | |
): State<R, E> { | |
switch (action.type) { | |
case "pending": { | |
return { status: "pending", data: null, error: null }; | |
} | |
case "resolved": { | |
return { status: "resolved", data: action.data, error: null }; | |
} | |
case "rejected": { | |
return { status: "rejected", data: null, error: action.error }; | |
} | |
default: { | |
throw new Error(`unhandled action type`); | |
} | |
} | |
}; | |
} | |
export default function useAsync<R, E = unknown>(intialState?: State<R, E>) { | |
const reducer = createReducer<R, E>(); | |
const [state, unsafeDispatch] = useReducer(reducer, { | |
status: "pending", | |
data: null, | |
error: null, | |
...intialState | |
}); | |
const safeDispatch = useSafeDispatch(unsafeDispatch); | |
const run = useCallback( | |
(promise: Promise<R>) => { | |
if (!promise || !promise.then) { | |
throw new Error("The argument must be a promise"); | |
} | |
safeDispatch({ type: "pending" }); | |
promise.then( | |
(data: R) => safeDispatch({ type: "resolved", data }), | |
(error: E) => safeDispatch({ type: "rejected", error }) | |
); | |
}, | |
[safeDispatch] | |
); | |
return { | |
...state, | |
run | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment