Skip to content

Instantly share code, notes, and snippets.

@zhenghaohe
Created January 24, 2021 01:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zhenghaohe/103d1d9bb006c942a87fc3d3bc2e1b0c to your computer and use it in GitHub Desktop.
Save zhenghaohe/103d1d9bb006c942a87fc3d3bc2e1b0c to your computer and use it in GitHub Desktop.
useAsync.ts
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