Skip to content

Instantly share code, notes, and snippets.

@adbutterfield
Last active June 8, 2020 06:11
Show Gist options
  • Save adbutterfield/a0d1dba48883329b5ef9da8f9467e45b to your computer and use it in GitHub Desktop.
Save adbutterfield/a0d1dba48883329b5ef9da8f9467e45b to your computer and use it in GitHub Desktop.
React hook to get/post with axios
import { useState, useEffect, useReducer, Dispatch, SetStateAction } from 'react';
import axios, { AxiosResponse, AxiosInstance } from 'axios';
type dataFetchReducerState<R = any> = {
isLoading: boolean;
hasError: boolean;
results?: R;
};
type dataFetchReducerAction = {
type: 'REQUEST_INIT' | 'REQUEST_SUCCESS' | 'REQUEST_FAILURE' ;
payload?: any;
};
const createDataFetchReducer = <R>() => (state: dataFetchReducerState<R>, action: dataFetchReducerAction) => {
switch (action.type) {
case 'REQUEST_INIT':
return {
...state,
isLoading: true,
hasError: false,
};
case 'REQUEST_SUCCESS':
return {
...state,
isLoading: false,
hasError: false,
results: action.payload,
};
case 'REQUEST_FAILURE':
return {
...state,
isLoading: false,
hasError: true,
};
default:
throw new Error();
}
};
export const axiosInstance: AxiosInstance = axios.create({
baseURL: '/api/',
});
function useGet<R = any>(url: string = '', otherHeaders?: { [key: string]: string }): {state: dataFetchReducerState<R>, setRequestUrl: Dispatch<SetStateAction<string>>, dispatch: Dispatch<dataFetchReducerAction>} {
const [requestUrl, setRequestUrl] = useState<string>(url);
const dataFetchReducer = createDataFetchReducer<R>();
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
hasError: false,
});
useEffect(() => {
let didCancel = false;
const get = async () => {
if (requestUrl) {
dispatch({ type: 'REQUEST_INIT' });
try {
const response: AxiosResponse<R> = await axiosInstance.get(requestUrl, {
headers: {
...otherHeaders,
},
});
if (!didCancel && response?.status === 200) {
dispatch({ type: 'REQUEST_SUCCESS', payload: response.data });
} else {
throw new Error('Status not 200');
}
} catch (error) {
if (!didCancel) {
dispatch({ type: 'REQUEST_FAILURE' });
}
}
}
};
get();
return () => {
didCancel = true;
};
}, [requestUrl]);
return { state, setRequestUrl, dispatch };
}
function usePost<R = any>(url: string, otherHeaders?: { [key: string]: string }): [dataFetchReducerState<R>, Dispatch<SetStateAction<any>>] {
const [postData, setPostData] = useState<any>();
const dataFetchReducer = createDataFetchReducer<R>();
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
hasError: false,
});
useEffect(() => {
let didCancel = false;
let timeout: any;
const post = async () => {
if (postData) {
dispatch({ type: 'REQUEST_INIT' });
try {
const response: AxiosResponse<R> = await axiosInstance.post(url, postData, {
headers: {
...otherHeaders,
},
});
if (!didCancel && response?.status === 200) {
dispatch({ type: 'REQUEST_SUCCESS', payload: response.data });
} else {
throw new Error('Status not 200');
}
} catch (error) {
if (!didCancel) {
dispatch({ type: 'REQUEST_FAILURE' });
}
}
}
};
post();
return () => {
didCancel = true;
if (timeout) {
clearTimeout(timeout);
}
};
}, [postData]);
return [state, setPostData];
}
// You can define all of your requests here as hooks
// That way, you'll know all the APIs that your application is using by just looking at this one file
// Define the return type for your requests
type someRequestReturnType = {
someProp: string;
someOtherProp: number[];
};
// Using this in a component will get the resource on page load
const useGetSomeResource = useGet<someRequestReturnType>('/some-endpoint');
// Define the return type for your requests
type setRequestUrlReturnType = {
someProp: string;
someOtherProp: number[];
};
// Sometimes you will want to make your API calls dynamic, based on some argument
// You can handle that like this:
const useGetSomeResourceBasedOnArguments = (): [dataFetchReducerState<setRequestUrlReturnType>, (someArg: string) => void] => {
// Initialize the useGet hook here
const { state, setRequestUrl } = useGet<setRequestUrlReturnType>();
// Define your own function which will call setRequestUrl from useGet
const makeRequest = (someArg: string) => setRequestUrl(`/some-other-endpoint/${someArg}`);
// Return state and your function however you like
return [state, makeRequest];
};
export {
useGetSomeResource,
useGetSomeResourceBasedOnArguments,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment