Вот как выглядит моя реализация useApi
:
// libs
import axios from 'axios';
import { useReducer, useRef } from 'react';
export const actionTypes = {
SET_LOADING: 'SET_LOADING',
SET_DATA: 'SET_DATA',
SET_ERROR: 'SET_ERROR',
};
export const fetchData = async (dispatch, cancelToken, action, params) => {
dispatch({ type: actionTypes.SET_LOADING });
return action(params, cancelToken)
.then(response => response.data)
.then(payload => {
dispatch({ type: actionTypes.SET_DATA, payload });
return payload;
})
.catch(error => {
if (!axios.isCancel(error)) {
dispatch({ type: actionTypes.SET_ERROR, error });
throw error;
}
});
};
const initialState = { isLoading: false, payload: {}, error: null };
export const reducer = (state, action) => {
const { type, payload, error } = action;
switch (type) {
case actionTypes.SET_LOADING:
return { ...state, isLoading: true, error: null };
case actionTypes.SET_DATA:
return { ...state, isLoading: false, error: null, payload };
case actionTypes.SET_ERROR:
return { ...state, isLoading: false, error };
default:
return state;
}
};
/**
* Reusable hook to make api calls using a function (action).
* It handles cancellation of previous requests automatically.
*
* @typedef State
* @type {object}
* @property {object} payload - Api response.
* @property {boolean} isLoading - status of Api call.
* @property {object} error - error object in case of failed call.
*
* @typedef ExecuteAction
* @type {function}
* @param {object} params - params to pass to action
* @returns {Promise} - resolves with payload
*
* @typedef useApi
* @param {function} action
* @returns [State, ExecuteAction, cleanupAction]
*/
const useApi = action => {
const [state, dispatch] = useReducer(reducer, initialState);
const axiosSource = useRef(null);
const cleanupAction = () => {
if (axiosSource.current) {
axiosSource.current.cancel('Cleaned up previous request.');
}
};
const executeAction = (params = {}) => {
cleanupAction();
axiosSource.current = axios.CancelToken.source();
return fetchData(dispatch, axiosSource.current.token, action, params);
};
if (!action || typeof action !== 'function') {
throw Error('Missing action || type of action is not function.');
}
return [state, executeAction, cleanupAction];
};
export default useApi;
Вы можете использовать ее в своем компоненте следующим образом:
const getData = (params, cancelToken) => axios.post('some url', params, { cancelToken });
const SomeComponent = () => {
const [state, fetchData, cleanup] = useApi(getData);
const { isLoading, error, payload } = state;
useEffect(() => {
fetchData({ key: 'some params to pass to getData action' });
return cleanup;
})
}
Вы можете настроить useApi
как вам как.