import { useReducer, useCallback, useRef, useEffect, MutableRefObject } from 'react' import { actionTypeEnum, IState, IAction, Unpacked, ErrorType } from './types' /** * @description reducer function * @author nazi * @date 2021-03-07 * @template T * @param {T} state * @param {IAction} action * @returns {(IState | ErrorType)} */ function reducer> (state: T, action: IAction): IState | ErrorType { switch (action.type) { case actionTypeEnum.REQUEST_INIT: return { data: null, ...state, loading: true, error: null, } case actionTypeEnum.REQUEST_SUCCESS: return { ...state, loading: false, error: null, data: action.payload, } case actionTypeEnum.REQUEST_FAILURE: return { ...state, loading: false, error: action.error, } default: throw new Error('error') } } /** * @description request function * @author nazi * @date 2021-03-07 * @template T * @param {T} instance * @param {(action: IAction>) => void} dispatch * @param {MutableRefObject} currentIndex * @returns {Promise>} */ async function request any> ( instance: T, dispatch: (action: IAction>) => void, currentIndex: MutableRefObject, ): Promise> { const prevCurrentIndex = currentIndex.current try { dispatch({ type: actionTypeEnum.REQUEST_INIT }) const result = await instance() if (prevCurrentIndex === currentIndex.current) { dispatch({ type: actionTypeEnum.REQUEST_SUCCESS, payload: result }) } return result } catch (error) { if (prevCurrentIndex === currentIndex.current) { dispatch({ type: actionTypeEnum.REQUEST_FAILURE, error }) } console.error(error) return error } } /** * @description main function * @author nazi * @date 2021-03-07 * @template T * @param {T} instance * @param {IState>>} [initialState={}] * @returns */ function useRequest any> ( instance: T, initialState: IState>> = {}, ) { const currentIndex = useRef(0) const [state, dispatch] = useReducer(reducer, initialState) const requestCallback = (...args: Parameters) => { currentIndex.current += 1 return request((): Unpacked> => instance(...args), dispatch, currentIndex) } useEffect(() => { return () => { currentIndex.current += 1 } }, []) const memoizedRequestCallback = useCallback(requestCallback, []) return [state, memoizedRequestCallback] } export default useRequest