Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
React hook for handling promise pattern
import React, { useReducer, useCallback } from 'react';
export type Service<R, P extends any[]> = (...args: P) => Promise<R>;
export type RequestState<R, P> = {
data?: R;
error?: any;
pending: boolean;
fulfilled: boolean;
rejected: boolean;
runArgs?: P;
};
export type RequestAction<R, P> =
| { type: 'request'; payload: P }
| { type: 'success'; payload: R }
| { type: 'failure'; payload: string };
function createReducer<R, P>() {
return function reducer(
state: RequestState<R, P>,
action: RequestAction<R, P>
): RequestState<R, P> {
switch (action.type) {
case 'request':
return {
...state,
error: null,
pending: true,
fulfilled: false,
rejected: false,
runArgs: action.payload,
};
case 'success':
return {
data: action.payload,
error: null,
pending: false,
fulfilled: true,
rejected: false,
};
case 'failure':
return {
...state,
error: action.payload,
pending: false,
fulfilled: false,
rejected: true,
};
}
};
}
export function useRequest<R, P extends any[]>(
asyncTask: Service<R, P>,
options?: {
// autoFirstRun?: boolean;
// passArgs?: P;
}
) {
// const {autoFirstRun = false, passArgs} = options || {};
const reducer = createReducer<R, P>();
const [state, dispatch] = useReducer<
React.Reducer<RequestState<R, P>, RequestAction<R, P>>
>(reducer, {
// data: null,
// error: null,
pending: false,
fulfilled: false,
rejected: false,
});
const requestActions = {
run: useCallback(
async (...args: P) => {
dispatch({
type: 'request',
payload: args,
});
try {
// then 패턴 데신에 await을 쓴 이유는 일반 함수일 경우에도 동작하도록
const data = await asyncTask(...args);
dispatch({
type: 'success',
payload: data,
});
return data;
} catch (e) {
dispatch({
type: 'failure',
payload: e,
});
throw e;
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[dispatch]
),
};
return [state, requestActions] as const;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment