Skip to content

Instantly share code, notes, and snippets.

@danecando
Last active Jul 14, 2022
Embed
What would you like to do?
A utility hook for managing async effects in a component
import * as React from 'react';
enum Async {
START,
SUCCESS,
FAILURE,
}
interface AsyncState<T> {
isLoading: boolean;
error?: unknown;
isComplete: boolean;
value?: T;
}
type AsyncAction<T> =
| {
type: Async.START;
}
| {
type: Async.SUCCESS;
payload: { value?: T };
}
| {
type: Async.FAILURE;
payload: { error: unknown };
};
interface ActionCreators<T> {
start: () => void;
success: (value?: T) => void;
failure: (error: unknown) => void;
}
function reducer<T>(
state: AsyncState<T>,
action: AsyncAction<T>
): AsyncState<T> {
switch (action.type) {
case Async.START:
return {
...state,
isLoading: true,
isComplete: false,
};
case Async.SUCCESS:
return {
isLoading: false,
error: undefined,
isComplete: true,
value: action.payload.value,
};
case Async.FAILURE:
return {
...state,
isLoading: false,
error: action.payload.error,
};
}
}
export function useAsyncState<T>(
initialState?: Partial<AsyncState<T>>
): [AsyncState<T>, ActionCreators<T>] {
const [state, dispatch] = React.useReducer<
React.Reducer<AsyncState<T>, AsyncAction<T>>
>(reducer, {
isLoading: false,
error: undefined,
isComplete: false,
...initialState,
});
const actionCreators = React.useMemo(
() => ({
start: () => dispatch({ type: Async.START }),
success: (value?: T) =>
dispatch({ type: Async.SUCCESS, payload: { value } }),
failure: (error: unknown) =>
dispatch({ type: Async.FAILURE, payload: { error } }),
}),
[dispatch]
);
return [state, actionCreators];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment