Skip to content

Instantly share code, notes, and snippets.

@kthompson
Last active December 5, 2023 08:25
Show Gist options
  • Save kthompson/aa7d114ae6501672deb1021ac33e4c62 to your computer and use it in GitHub Desktop.
Save kthompson/aa7d114ae6501672deb1021ac33e4c62 to your computer and use it in GitHub Desktop.
type AsyncState<T> = {
status: 'pending'
} | { status: 'resolved', data: T } | { status: 'idle' } | { status: 'rejected', error: unknown }
type AsyncAction<T> = { type: 'pending' } | { type: 'resolved'; data: T } | { type: 'rejected', error: unknown }
const asyncReducer = <T>(state: AsyncState<T>, action: AsyncAction<T>) => {
switch (action.type) {
case 'pending':
return { status: 'pending' };
case 'resolved':
return { status: 'resolved', data: action.data };
case 'rejected':
return { status: 'rejected', error: action.error };
}
}
type UseAsync<T> = AsyncState<T> & { run(promise: Promise<T>): void }
function useAsync<T>(initialState?: AsyncState<T>) {
const [state, unsafeDispatch] = React.useReducer(asyncReducer, initialState ?? { status: 'idle' });
const dispatch = useSafeDispatch(unsafeDispatch)
const run = React.useCallback(promise => {
dispatch({ type: 'pending' });
promise.then(data => dispatch({ type: 'resolved', data }), error => dispatch({ type: 'rejected', error }));
})
return { ...state, run }
}
function useSafeDispatch(dispatch) {
const mountedRef = React.useRef(false);
React.useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
});
return React.useCallback((...args) => {
if (mountedRef.current) {
dispatch(...args)
}
}, [dispatch])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment