Data fetching in React! is easy
Tags: javascript, axios, react
Published: 2024-03-02 03:01:17

Hi everyone
Today I wanna show you an HTTP client that I use in my projects and how it helped me during RESTful API calls.

If you are asking why I did such a thing? Well, using pure Axios is frustrating, fetch API is trash, and React Query (a.k.a TanStack Query) is just not for me.

Oh, guess what! I handle loading state as well ...

So let's how I make my HTTP API calls
**MyComponent.tsx**
```typescript

const MyComponent = () => {
	const { getProducts, dataProducts, loadingProducts } = useAxios({
		url: '/url',
		name: 'Products',
	})

	useEffect(() => {
		getProducts()
	}, [])

	return <div>{dataProducts && <div>Show products here ...</div>}</div>
}

export default MyComponent

```


Very simple! You can have post requests and maybe add other methods as well. But I decided to keep it simple.

So do you need a put request?
Here is how to use it:

```typescript
const { putReqByUrl } = useAxios({
		url: '/url',
		name: 'Products',
	})

const res = await putReqByUrl('/1234')

```


Or maybe you have to fetch from many URLs?
Ok:
```typescript
const { getReq } = useAxios({
	url: '/url',
})
const { getReq: getOtherUrl } = useAxios({
	url: '/other-url',
})
const { getAnotherOne, postAnotherOne } = useAxios({
	url: '/other-url',
	name: 'AnotherOne',
})

await getReq()
await getOtherUrl()
await getAnotherOne()
await postAnotherOne()
```

Yeah yeah you need queries
```typescript
await getReq({ page: 2 }) 
// It will be
// GET method into -> your-super-url?page=2
// You can add more no problem
```


So this is what my custom hook looks like
**useAxios.ts**

```typescript
import axios from 'axios'

const axiosInstance = axios.create()
axiosInstance.defaults.baseURL = 'base url here'

interface Props {
	url: string
	data?: any
	name?: string | null
	notif?: boolean
	headers?: any
}

const objectToQueryString = (obj: any, withQuestionMark = true) => {
	let r: any = []

	Object.keys(obj).forEach((key) => {
		if (obj[key] !== null && obj[key] !== undefined) {
			r.push(key + `=` + obj[key])
		}
	})

	return r.length > 0 ? `${withQuestionMark ? '?' : ''}${r.join('&')}` : null
}

const useAxios = ({
	notif = true,
	url,
	headers,
	data: userData,
	name = null,
}: Props) => {
	const [loading, setLoading] = useState(false)
	const [data, setData] = useState(null)
	const [response, setResponse] = useState({})
	const [status, setStatus] = useState(0)
	const [errorMessage, setErrorMessage] = useState(null)
	const [error, setError] = useState(null)

        // your custom alert hook if you have
        // whenever you make an http request, you will have toast 
        // and alarm stuff
	const alert = useAlert() 

	const request = async (
		method: string,
		payload = {},
		noLoading = false,
		userUrl?: string
	) => {
		setLoading(true)

		const axiosObj = {
			method,
			headers: headers,
			url: '',
			data: {},
		}

		if (method === 'GET') {
			if (objectToQueryString(payload)) {
				axiosObj.url = url + objectToQueryString(payload)
			} else {
				axiosObj.url = url
			}
		} else {
			axiosObj.url = url
			axiosObj.data = payload
		}

		if (userUrl) {
			axiosObj.url = userUrl
		}

		try {
			const res = await axiosInstance(axiosObj)

			setData(res.data.data)
			setResponse(res)
			setStatus(res.status)
			setLoading(false)

			// Add your alert here if you have any
			if (notif) {
				// TODO translate first then
				// alert({
					// type: 'success',
					// @ts-ignore
					// text: res.data.message,
				// })
			}

			return res.data
		} catch (e: any) {
			setResponse(e.response)
			setError(e)
			setStatus(e.response.status)
			setErrorMessage(e.response.data.message)
			setLoading(false)

			// Add your alert here if you have any
			if (notif) {
				// TODO translate first then
				// alert({
					// type: 'error',
					// text: e.response.data.message,
				// })
			}

			throw e
		}
	}

	const getReq = async (data: any, noLoading = false, url?: string) =>
		await request('GET', data, noLoading, url)
	const postReq = async (data: any, noLoading = false, url?: string) =>
		await request('POST', data, noLoading, url)
	const putReq = async (data: any, noLoading = false, url?: string) =>
		await request('PUT', data, noLoading, url)
	const patchReq = async (data: any, noLoading = false, url?: string) =>
		await request('PATCH', data, noLoading, url)
	const deleteReq = async (data: any, noLoading = false, url?: string) =>
		await request('DELETE', data, noLoading, url)

	const deleteReqByUrl = (url: string) => deleteReq(null, false, url)
	const putReqByUrl = (url: string, data: any) => putReq(data, false, url)
	const patchReqByUrl = (url: string, data: any) => patchReq(data, false, url)
	const getReqByUrl = (url: string, data: any) => getReq(data, false, url)
	const postReqByUrl = (url: string, data: any) => postReq(data, false, url)

	const returnObj: any = {
		error,
		data,
		response,
		status,
		errorMessage,
		loading,
		getReq,
		postReq,
		putReq,
		patchReq,
		deleteReq,
		deleteReqByUrl,
		putReqByUrl,
		getReqByUrl,
		patchReqByUrl,
		postReqByUrl,
	}

        // name sanitization
	if (name) {
		returnObj['get' + name] = getReq
		returnObj['post' + name] = postReq
		returnObj['put' + name] = putReq
		returnObj['patch' + name] = patchReq
		returnObj['delete' + name] = deleteReq
		returnObj['loading' + name] = loading
		returnObj['data' + name] = data
		returnObj['error' + name] = error
		returnObj['getByUrl' + name] = getReqByUrl
		returnObj['postByUrl' + name] = postReqByUrl
		returnObj['putByUrl' + name] = putReqByUrl
		returnObj['patchByUrl' + name] = patchReq
		returnObj['deleteByUrl' + name] = deleteReq
	}

	return returnObj
}
```

Thanks for reading guys!

And oh, you have read this post. So you can see the Vue.js version of this useAxios ;-)

Go and check it out:
[Easier way of data fetching with Axios in Vue3](https://dev.to/ataparvinghods/easier-way-of-data-fetching-with-axios-in-vue3-53jk)