-
-
Save zhenghaohe/103d1d9bb006c942a87fc3d3bc2e1b0c 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