Last active
June 8, 2020 06:11
-
-
Save adbutterfield/a0d1dba48883329b5ef9da8f9467e45b to your computer and use it in GitHub Desktop.
React hook to get/post with axios
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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