DEV Community

Ata Parvin Ghods
Ata Parvin Ghods

Posted on • Edited on

1 1 1 1 1

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

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

SurveyJS custom survey software

JavaScript Form Builder UI Component

Generate dynamic JSON-driven forms directly in your JavaScript app (Angular, React, Vue.js, jQuery) with a fully customizable drag-and-drop form builder. Easily integrate with any backend system and retain full ownership over your data, with no user or form submission limits.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay