Supercharge Network Performance in React Apps: Harnessing the Power of React Query and Axios

Supercharge Network Performance in React Apps: Harnessing the Power of React Query and Axios

Boosting Network Performance in React Apps with React Query and Axios Network requests play a vital role in the majority of today’s applications. However, often we overlook the importance of optimizing performance. In this article, we will explore an efficient code structure that prioritizes both code readability and network performance.

To make the most of this article, it is recommended to have a working knowledge of ReactJS, JavaScript, and a network request method like Fetch (which we will use as a baseline example)

Axios

Normally we use Fetch API for an API call, and the code would seem like this:

fetch('https://api.example.com/posts', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your_token_here'
  }
})
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Error:', error);
  });        

There are several problems using this approach:

  • You can notice how we are converting the data to JSON manually. Fetch does not automatically parse Data for us and this can create a level of code redundancy.
  • Fetch is not supported in older browsers such as Internet Explorer. While developing applications its important to consider users with all types of internet connections or devices.
  • Imagine a user with slow internet connection, they send some sort of request, and it keeps loading because the internet is slow. Now the user is frustrated and is trying to cancel it, but there is no way to cancel the request and hence, there is a chance that the network calls start to repeat, causing potential conflicts
  • Have you ever noticed how you do not login again and again on applications like facebook, instagram etc? Months pass by to the extend that we usually forget passwords. This is because of what we normally call as, refresh tokens. Access tokens (which are used to log us in) expire after a certain amount of time and hence refresh tokens are used to renew the lifetime of the access token. How is this achieved? Whenever an API call returns a 401 error (forbidden resource error), we want the our system to send another API call, with the refresh token to refresh the access token, and once it is done, resend the API request. Now this is basically 3 API calls at the same time which is an extremely lengthy process

Axios

Axios solves our problems here.

  • It is compatible with most browsers
  • Axios provides behind the scenes JSON parsing:


axios.get('https://api.example.com/posts', {
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your_token_here'
  }
})
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error('Error:', error);
  });        

  • It provides out of the box interceptor logic which helps us reduce our code and simply perform the refresh token functionality.

// Add a request interceptor
Axios.interceptors.request.use(
  (config) => {
    const token = localStorageService.getAccessToken();
    if (token) {
      config.headers["Authorization"] = "Bearer " + token;
    }
    return config;
  },
  (error) => {
    Promise.reject(error);
  }
);

//Add a response interceptor
Axios.interceptors.response.use(
  (response) => {
    return response;
  },
  function (error) {
    const originalRequest = error.config;

    if (
      error.response.status === 401 &&
      originalRequest.url === ".../oauth/token"
    ) {
      return Promise.reject(error);
    }

    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

     ...API LOGIC
    }
    return Promise.reject(error);
  }
);        

We can simply build a function returning a function to build such logic to avoid code repetition.

  • Axios provides out of the box Request cancellation mechanism

import axios from 'axios';

// Create a cancel token source
const cancelTokenSource = axios.CancelToken.source();

// Make the request with cancellation token
axios.get('https://api.example.com/data', {
  cancelToken: cancelTokenSource.token
})
  .then(response => {
    // Handle response
    console.log(response.data);
  })
  .catch(error => {
    // Handle error (including cancellation error)
    if (axios.isCancel(error)) {
      console.log('Request canceled:', error.message);
    } else {
      console.error('Error:', error);
    }
  });

// Cancel the request
cancelTokenSource.cancel('Request canceled by the user');        

API Layer

When I started coding, I used to keep all my API calls in the React JS component wherever I needed them. For example, lets say I am building a Social Media Application, and I needed Posts, I would be calling the API right within the “Posts.jsx” file where I am using the data.

Now imagine in the same example, you have a 100 API calls just like this one and they are all around the application. Then imagine you want to change your backend from NodeJS to Firebase for whatever reason, you would go through all those 100 files to make the corresponding changes.

Hence, to solve this problem, I use what’s called as an API Layer. We create a file in the “src” directory called services.js which includes all the network requests. This way, we can even reuse end points without code duplication.

So now all your API logic is in services.js

import axios from "axios";
import environmentVariables from "./../config/env";
import { FORBIDDEN_RESOURCE_ERROR } from "../config/constants";
const apiUrl = environmentVariables.API_URL;

export const login = async ({ userName, password }) => {
  try {
    const response = await axios.post(`${apiUrl}/user/login`, {
      username: userName,
      password: password,
    });
    localStorage.setItem("accessToken", response.data.accessToken);

    return {
      error: false,
      payload: response,
    };
  } catch (err) {
    return {
      error: true,
      message: err.message,
      payload: err,
      responseText: err.response.data.otherMessage,
    };
  }
};        

React Query

Now comes the best part. Normally, in React apps, the logic to perform loading states or error states could be as follows:

const MyComponent = () => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);
  const [data, setData] = useState([])

  useEffect(() => {
    setLoading(true)
    getDataFromApi().then(res => {
      // Assuming the response contains an error key
      if (res.error) {
         setError(true)
         setLoading(false)
      }

      setLoading(false)
      setData([...res.data])
    }).catch(err => {
         setError(true)
         setLoading(false)
      }
  }, [])

  return <div>...</div>
}        

Notice how this code will be repeated for every single API call.

Instead, look at this much cleaner and shorter version using React Query:

const MyComponent = () => {
  const {data, isError, isLoading} = useQuery('data')
  // Assuming the response contains an error key
  if (data && data.error) {
    return <div>An error occurred: {data.response}</div>;
  }
  return <div>...</div>
}        

Caching

Now imagine, your data is not changing at all, and you send network requests again and again only to get the same result. Similarly, in slow networks, you would want your data to remain intact. Which is where caching comes in. The ‘data’ key passed in the useQuery function basically says if the ‘data’ key does not change, then i will not resend an API request. Ofcourse after a period API calls will take place to ensure data isn’t changing on the backend.

Conclusion

The mechanism I provided improves your network performance by a lot. In the next article I will explain how to combine this entire functionality with Next JS to utilize server side logic and combine this to develop a high performant website that can run in slow internet also.

Great article! One thing I feel is missing is how to bring it all together. Either a link to github or just another code dump at the end showing how to use react-query with axios with interceptors.

To view or add a comment, sign in

More articles by Asad Zubair Bhatti

Others also viewed

Explore content categories