DEV Community

Ata Parvin Ghods
Ata Parvin Ghods

Posted on • Edited on

Data fetching in React! is easy

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


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

Enter fullscreen mode Exit fullscreen mode

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:

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

const res = await putReqByUrl('/1234')

Enter fullscreen mode Exit fullscreen mode

Or maybe you have to fetch from many URLs?
Ok:

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()
Enter fullscreen mode Exit fullscreen mode

Yeah yeah you need queries

await getReq({ page: 2 }) 
// It will be
// GET method into -> your-super-url?page=2
// You can add more no problem
Enter fullscreen mode Exit fullscreen mode

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

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
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)