Which is the best practise for using axios in React Project

Viewed 1479

I am using axios in my create-react-app. Which is the best way to use axios:

Method 1:

ajax.js

import axios from 'axios';
const axiosInstance = axios.create({});
export default axiosInstance;

app.js

import ajax from './ajax.js';
ajax.post('url');

Method 2:

ajax.js

import axios from 'axios';
class AjaxService{
    constructor(apiConfig){
        this.service = axios.create(apiConfig);
    }
    doGet(config){
        return this.service.get(config.url);
    }
    ...
}
export default AjaxService;

app.js:

import AjaxService from './ajax';
const service1 = new AjaxService();
service.doGet({url:'url'});

app2.js

import AjaxService from './ajax';
const service2 = new AjaxService();
service.doGet({url:'url'});

In method 2, we have to initialize the service wherever we make a call, which may or may not be a best practice. If we follow method 2, Is there a way to make it as a common service across the application?

3 Answers

i've seen a way in here and i came up with another solution like i explained below:

1 - i created my service with axios

import axios from 'axios';

const instance = axios.create({
    baseURL: process.env.REACT_APP_BASE_URL
    // headers: { 'X-Custom-Header': 'foobar' }
});

// Add a request interceptor
instance.interceptors.request.use(
    (config) => {
        // Do something before request is sent
        return config;
    },
    (error) => {
        // Do something with request error
        return Promise.reject(error);
    }
);

// Add a response interceptor
instance.interceptors.response.use(
    (response) => {
        // Any status code that lie within the range of 2xx cause this function to trigger
        // Do something with response data
        return response;
    },
    (error) => {
        // Any status codes that falls outside the range of 2xx cause this function to trigger
        // Do something with response error
        return Promise.reject(error);
    }
);

export default instance;

2- i use that service to create a function for api call. in here i can add a new AbortController for later use in useEffect.

import axios from 'services/request';

export function getMarket(type, callBack) {
    const controller = new AbortController();

    axios
        .get(`https://dev.zh1.app/api/market/map?type=${type}`, {
            signal: controller.signal
        })
        .then((res) => {
            callBack(true, res.data);
        })
        .catch((res) => {
            callBack(false, res.response);
        });

    return controller;
}

export default {
    getMarket
};

3- in the hooks folder i created a hook called useApi. the controller from step 2 used in here. if you check the link above you can see the author add request function because you may have some props to pass to api call. i think it is valid but ugly. so i decided to create a closure for useApi to pass any params i want to the Axios in step 2.

import { useEffect, useState } from 'react';

// in useStrict mode useEffect call twice but will not in production

export default function useApi(apiFunc) {
    return function useApiCall(...params) {
        const [result, setResult] = useState(null);
        const [error, setError] = useState(null);
        const [loading, setLoading] = useState(false);

        const apiCall =  useCallback(() => {
            setLoading(true);
            // controller is new AbortController which set each api function
            const controller = apiFunc(...params, (ok, data) => {
                setLoading(false);
                if (ok) {
                    setResult(data);
                } else {
                    setError(data.message);
                }
            });
            return () => {
                controller.abort();
            };
        }, []);

        useEffect(() => {
           apiCall();
        }, []);

        return {result, loading, error, [apiFunc.name]: apiCall};
    };
}

4- finally in my react component

import { IconButton } from '@mui/material';
import useApi from '@share/hooks/useApi';
import { Refresh } from '@share/icons';
import { getCaptcha as CaptchaApi } from 'api/oauth/captcha';
import CaptchaStyle from './style';

export default function Captcha() {
    const { result: captcha, getCaptcha } = useApi(CaptchaApi)();

    return (
        <CaptchaStyle>
            <img src={`data:image/png;base64,${captcha?.base64}`} alt="captcha" />
            <IconButton onClick={getCaptcha}>
                <Refresh />
            </IconButton>
        </CaptchaStyle>
    );
}

i think this approach i quite good and if you dont need to pass any props just call useApi([yourfunction])() with empty function.

and you can have access to the function inside of useApi if you need to call it again.

It totally depends on your project. If your project relies more on the function component then go ahead and use the first approach.

If you use classes for the majority of your components go for the second approach.

I generally use the first approach, it's easy and avoids this altogether. Also, it's easy to target multiple instances.

// Ajax.js file

import axios from "axios";

export function updateData=(body,callback){
le url= 'your api to call'
  axios
    .put(url, body)
    .then((response) => response.data)
    .then((res) => {
      callback(res);
    })
    .catch((error) => {
      console.log(error);

      callback('error occurred');
    });
}

// app.js file

import {updateData} from './ajax.js'

//Place your code where you need
 updateData(yourBodyToPass,res=>{
 //Stuff after the response
 )

Note:- pass your data as first argument and get response of api from second

Related