Skip to content

Instantly share code, notes, and snippets.

@Granipouss
Last active May 6, 2022 18:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Granipouss/0f382fa8dc532a4252ab6828567a50c5 to your computer and use it in GitHub Desktop.
Save Granipouss/0f382fa8dc532a4252ab6828567a50c5 to your computer and use it in GitHub Desktop.
import { useCallback, useReducer, Reducer, useRef, useEffect } from 'react';
export const useIsMounted = () => {
const isMounted = useRef<boolean>(false);
useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
};
}, []);
return () => isMounted.current;
};
type AsyncState<T> = {
isLoading: boolean;
isDone: boolean;
error?: Error;
result?: T;
};
type AsyncMessage<T> =
| { type: 'RESET' }
| { type: 'START' }
| { type: 'SUCCESS'; payload: T }
| { type: 'FAILURE'; payload: Error };
const defaultAsyncState = {
isLoading: false,
isDone: false,
};
function asyncStateReducer<T>(state: AsyncState<T>, message: AsyncMessage<T>) {
switch (message.type) {
case 'RESET':
return defaultAsyncState;
case 'START':
return { ...defaultAsyncState, isLoading: true };
case 'SUCCESS':
return { ...state, isLoading: false, isDone: true, result: message.payload };
case 'FAILURE':
return { ...state, isLoading: false, error: message.payload };
default:
return state;
}
}
export const useAsync = <TResult, TArgs extends any[]>(
fn: (...args: TArgs) => Promise<TResult>,
) => {
const [state, dispatch] = useReducer<Reducer<AsyncState<TResult>, AsyncMessage<TResult>>>(
asyncStateReducer,
defaultAsyncState,
);
const isMounted = useIsMounted();
const call = useCallback(
(...args) => {
dispatch({ type: 'START' });
fn(...(args as TArgs))
.then(payload => {
if (isMounted()) {
dispatch({ type: 'SUCCESS', payload });
}
})
.catch(payload => {
if (isMounted()) {
dispatch({ type: 'FAILURE', payload });
}
});
},
[fn],
);
const reset = useCallback(() => dispatch({ type: 'RESET' }), []);
return [call, { ...state, reset, call }, state.result] as [
(...args: TArgs) => void,
AsyncState<TResult> & { call: (...args: TArgs) => void; reset: () => void },
TResult
];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment