Saturday, 9 October, 2021 UTC


Summary

Introduction
More often than not, you will need to make network requests to an API when building a web or mobile application. You can make these network requests to authenticate a user, update a resource, or retrieve a resource from your own server or third party APIs. The Fetch API comes in handy if you want to make API requests in a browser environment.
React Native also has a built-in Fetch API similar to the browser’s, specifically for networking with an API from your mobile application. However, there are alternative libraries, such as Axios, that you can use instead of relying on the native Fetch API.
The built-in fetch API might suffice if you only want to retrieve a resource from the server. Axios may be a better option for more complex networking requirements because it comes with additional features, such as the interception of network requests and responses.
Additionally, among other reasons, most developers prefer Axios over the built-in fetch API because of its isomorphic nature and out-of-the-box JSON transformation.
In this article, you will learn how to manage API requests using Axios in a React Native application.
Introduction to Axios
Axios is a popular, isomorphic HTTP client. That means it can run in the browser and the Node runtime environment. As a result, you can use the same codebase for making API requests in Node, in the browser, and in React Native.
Axios has several features such as support for the Promise API, automatic JSON transformation, and interception of network requests and responses, among others.
Setting up a simple React Native application using the Expo CLI
We will use a simple React Native application set up using Expo’s managed workflow for this tutorial.
If you have a React Native application set up already, you can continue to the next section. Otherwise, there are setup instructions in the React Native documentation for Expo’s managed workflow that can get you up and running within minutes.
Introduction to Axios
Axios is one of the easiest HTTP clients to learn and use. Making an API request is as simple as passing a configuration object to Axios or invoking the appropriate method with the necessary arguments. You will learn the basics of Axios in this section.
The features highlighted in the following sub-sections are the most common features you will use when working with Axios.

How to install Axios

Depending on which package manager you use, type one of the commands below on a terminal window and hit return to install Axios:
// axios
npm install axios

// yarn
yarn add axios

How to make requests to an API using Axios

When making a call to an API using Axios, you can pass a configuration object to Axios or invoke a method for the corresponding CRUD operation you want to perform.
For example, you can make a GET request to the /api/users endpoint in one of the following two ways:
import axios from 'axios';
const baseUrl = 'https://reqres.in';

// Passing configuration object to axios
axios({
  method: 'get',
  url: `${baseUrl}/api/users/1`,
}).then((response) => {
  console.log(response.data);
});


// Invoking get method to perform a GET request
axios.get(`${baseUrl}/api/users/1`).then((response) => {
  console.log(response.data);
});
You can also use async/await instead of using Promise chaining like in the above example.
There are several other fields such as baseURL, transformRequest, transformResponse, and headers, among others, which you can include in the configuration object you pass to Axios:
import axios from 'axios';
const baseUrl = 'https://reqres.in';

// Passing configuration object to axios
const fetchUser = async () => {
  const configurationObject = {
    method: 'get',
    url: `${baseUrl}/api/users/1`,
  };
  const response = await axios(configurationObject);
  console.log(response.data);
};

// Invoking get method to perform a GET request
const fetchUser = async () => {
  const url = `${baseUrl}/api/users/1`;
  const response = await axios.get(url);
  console.log(response.data);
};
Unlike the built-in Fetch API, Axios will convert the response to JSON for you out of the box.

How to make multiple concurrent API requests using Axios

You can use the Promise.all or Promise.allSettled method of the Promise API with Axios to make multiple concurrent API requests from a React Native application.
All the API requests will be successful in the code snippet below; change the URI passed to the axios.get method to non-existent ones to see what happens if some of the requests are not successful:
const concurrentRequests = [
      axios.get(`${baseUrl}/api/users/1`),
      axios.get(`${baseUrl}/api/users/2`),
      axios.get(`${baseUrl}/api/users/3`),
    ];
   // Using Promise.all
    Promise.all(concurrentRequests)
      .then((result) => {
        console.log(result);
      })
      .catch((err) => {
        console.log(err);
      });
    // Using Promise.allSettled
    Promise.allSettled(concurrentRequests)
      .then((result) => {
        console.log(result);
      })
      .catch((err) => {
        console.log(err);
      });
Take note that the Promise.all method immediately rejects if one of the input Promises rejects. Use Promise.all if you want to see all or none of the API requests be successful.
Promise.allSettled, on the other hand, waits for all of the input Promises to either reject or fulfill. Then, you can check the fulfilled or rejected status of each response object.

How to abort network request in Axios

Axios provides functionality for aborting network requests. A typical use case of this feature in React Native is the cancellation of network requests in the useEffect hook when a component is unmounted while data is still in flight.
You can read the code snippet below to understand how to use this functionality:
useEffect(() => {
    const source = axios.CancelToken.source();
    const url = `${baseUrl}/api/users/${userId}`;
    const fetchUsers = async () => {
      try {
        const response = await axios.get(url, { cancelToken: source.token });
        console.log(response.data);
      } catch (error) {
        if(axios.isCancel(error)){
          console.log('Data fetching cancelled');
        }else{
         // Handle error
        }
      }
    };
    fetchUsers();
    return () => source.cancel("Data fetching cancelled");
  }, [userId]);

How to create an instance of Axios

You can also create an instance of Axios with a custom configuration. Then, use the methods exposed by the instance to make network requests.
Axios will merge the configuration object passed while creating the instance with the configuration passed to the instance method:
const axiosInstance = axios.create({ baseURL: 'https://reqres.in/' });

axiosInstance.get('api/users/1').then((response) => {
  console.log(response.data);
});
Using Axios with React Native to manage API requests
In this section, you will learn to manage API requests using Axios in a React Native application. You will use Axios to perform a simple CRUD (Create, Read, Update, and Delete) operation.

How to manage API keys in React Native

Most third party APIs require secret credentials to access. It is not a good idea to keep your secret API keys in your source code on the client side. If you do, anybody who inspects the code for your bundled application will have access to your private key.
One of the recommended methods of managing your API key is to create an orchestration layer between the third party API and your application. For example, you can use a serverless function to securely access your API key.
Your app will make a call to the endpoint exposed by the serverless function. The serverless function will securely access your API key, make a call to the third party API, retrieve the resource you need, and relay it to your mobile application.

Managing application state in the network request-response cycle

When you initiate a network request to an API, the request will either succeed or fail. Therefore, it is important to keep track of the different states of your app from the time of requesting until you receive a response from the server.
While the data is still in flight, you can display a loading indicator. If the CRUD operation is successful, you show a “success” message to the user. If it fails, you display an appropriate error message.
This is important because a client using your application might have a slow internet connection or no internet access. The API server may sometimes experience downtime. Tracking the state of your application and displaying appropriate messages will provide a good user experience.
We shall use the reqres REST API in this article. It is a placeholder API comprising of dummy data. You can play with the endpoints using API testing tools such as Postman or Insomnia.

How to make GET request using Axios in React Native

In this section, we shall make a GET request to the /api/users endpoint to retrieve a user. GET is the HTTP method you use if you want to request a resource from the server.
We are storing the user ID in state as shown in the code snippet below. You can change the user ID inside the onPress event handler attached to the Load User button. Changing the user ID will trigger a GET request to the API inside the useEffect hook.
After triggering a network request, we display a loading indicator on the screen. If we fetch the data successfully, we update state and remove the loading indicator. If we fail to retrieve the data for some reason, we stop the loading indicator and display an appropriate error message.
We abort the network request in the cleanup function if the user decides to close the app before getting a response from the server. Check the return value of the effect function in the useEffect hook.
Here’s what the code for this looks like in the App.js component:
import axios from "axios";
import React, { useState, useEffect } from "react";
import {
  StyleSheet,
  Text,
  ScrollView,
  View,
  Button,
  Image,
  Platform,
} from "react-native";
import Constants from "expo-constants";
const baseUrl = "https://reqres.in";
function User({ userObject }) {
  return (
    <View>
      <Image
        source={{ uri: userObject.avatar }}
        style={{ width: 128, height: 128, borderRadius: 64 }}
      />
      <Text style={{ textAlign: "center", color: "white" }}>
        {`${userObject.first_name} ${userObject.last_name}`}
      </Text>
    </View>
  );
}
export default function App() {
  const [userId, setUserId] = useState(1);
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [hasError, setErrorFlag] = useState(false);
  const changeUserIdHandler = () => {
    setUserId((userId) => (userId === 3 ? 1 : userId + 1));
  };
  useEffect(() => {
    const source = axios.CancelToken.source();
    const url = `${baseUrl}/api/users/${userId}`;
    const fetchUsers = async () => {
      try {
        setIsLoading(true);
        const response = await axios.get(url, { cancelToken: source.token });
        if (response.status === 200) {
          setUser(response.data.data);
          setIsLoading(false);
          return;
        } else {
          throw new Error("Failed to fetch users");
        }
      } catch (error) {
        if(axios.isCancel(error)){
          console.log('Data fetching cancelled');
        }else{
          setErrorFlag(true);
          setIsLoading(false);
        }
      }
    };
    fetchUsers();
    return () => source.cancel("Data fetching cancelled");
  }, [userId]);
  return (
    <ScrollView contentContainerStyle={styles.container}>
      <View style={styles.wrapperStyle}>
        {!isLoading && !hasError && user && <User userObject={user} />}
      </View>
      <View style={styles.wrapperStyle}>
        {isLoading && <Text> Loading </Text>}
        {!isLoading && hasError && <Text> An error has occurred </Text>}
      </View>
      <View>
        <Button
          title="Load user"
          onPress={changeUserIdHandler}
          disabled={isLoading}
          style={styles.buttonStyles}
        />
      </View>
    </ScrollView>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "dodgerblue",
    alignItems: "center",
    justifyContent: "center",
    marginTop: Platform.OS === "ios" ? 0 : Constants.statusBarHeight,
  },
  wrapperStyle: {
    minHeight: 128,
  },
  buttonStyles: {
    padding: 100,
  },
});
In the above component, we use the useEffect hook to perform side effects, like fetching data from an API. However, this might change in future versions of React. You can check the React documentation for more about React Suspense; it is a data fetching feature that will land in a stable release of React very soon.

How to make POST request using Axios in React Native

In this section, you will learn how to make a POST request. POST is the HTTP method you use to send data to the server for updating or creating a resource.
The placeholder API we are using exposes the /api/users endpoint for creating a resource. You will get a response with a 201 status code after successfully creating the resource.
Making a POST request in Axios is similar to making a GET request. Most of the time, POST requests are made with user-generated data submitted using a form. The submitted data can be from log in, sign up, or feedback forms from your clients. Such data requires validation on the client side before it is submitted.
You can use one of the form packages for data validation when building a complex applications. Most of the packages are well architected and optimized, and have a great community behind them. However, before integrating a library in your application, explore the tradeoffs. Especially the additional bundle size you are adding to your app, and potential security vulnerabilities it might introduce.
There are two main React packages for managing forms. These packages are Formik and React Hook Form. You can find plenty of articles on form validation in React if you are interested.
We have a React Native form for the user’s full name and email in the code snippet below. Both TextInput components are controlled components. Ideally, as the user fills the form, you perform data validation in real time. However, that is not the case here, because form data validation is outside the scope of this article.
After clicking the submit button, the TextInput fields and the submit button are disabled before you display a message to show you are creating the resource. Disabling the submit button ensures the user doesn’t make multiple submissions.
After successfully submitting a POST request, you display a success message to the user:
import axios from "axios";
import React, { useState } from "react";
import {
  StyleSheet,
  Text,
  ScrollView,
  View,
  Button,
  Platform,
  TextInput,
} from "react-native";
import Constants from "expo-constants";

const baseUrl = "https://reqres.in";

export default function App() {
  const [fullName, setFullName] = useState("");
  const [email, setEmail] = useState("");
  const [isLoading, setIsLoading] = useState(false);

  const onChangeNameHandler = (fullName) => {
    setFullName(fullName);
  };

  const onChangeEmailHandler = (email) => {
    setEmail(email);
  };

  const onSubmitFormHandler = async (event) => {
    if (!fullName.trim() || !email.trim()) {
      alert("Name or Email is invalid");
      return;
    }
    setIsLoading(true);
    try {
      const response = await axios.post(`${baseUrl}/api/users`, {
        fullName,
        email,
      });
      if (response.status === 201) {
        alert(` You have created: ${JSON.stringify(response.data)}`);
        setIsLoading(false);
        setFullName('');
        setEmail('');
      } else {
        throw new Error("An error has occurred");
      }
    } catch (error) {
      alert("An error has occurred");
      setIsLoading(false);
    }
  };

  return (
    <ScrollView contentContainerStyle={styles.container}>
      <View>
        <View style={styles.wrapper}>
          {isLoading ? (
            <Text style={styles.formHeading}> Creating resource </Text>
          ) : (
            <Text style={styles.formHeading}>Create new user</Text>
          )}
        </View>
        <View style={styles.wrapper}>
          <TextInput
            placeholder="Full Name"
            placeholderTextColor="#ffffff"
            style={styles.input}
            value={fullName}
            editable={!isLoading}
            onChangeText={onChangeNameHandler}
          />
        </View>
        <View style={styles.wrapper}>
          <TextInput
            placeholder="Email"
            placeholderTextColor="#ffffff"
            style={styles.input}
            value={email}
            editable={!isLoading}
            onChangeText={onChangeEmailHandler}
          />
        </View>
        <View>
          <Button
            title="Submit"
            onPress={onSubmitFormHandler}
            style={styles.submitButton}
            disabled={isLoading}
          />
        </View>
      </View>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#252526",
    alignItems: "center",
    justifyContent: "center",
    marginTop: Platform.OS === "ios" ? 0 : Constants.statusBarHeight,
  },
  formHeading: {
    color: "#ffffff",
  },
  wrapper: {
    marginBottom: 10,
  },
  input: {
    borderWidth: 2,
    borderColor: "grey",
    minWidth: 200,
    textAlignVertical: "center",
    paddingLeft: 10,
    borderRadius: 20,
    color: "#ffffff",
  },
  submitButton: {
    backgroundColor: "gray",
    padding: 100,
  },
});

How to make a PUT request using Axios in React Native

Updating a resource requires either the PUT or PATCH method, though I will focus on PUT.
If a resource exists, using the PUT method completely overwrites it, and creates a new resource if it doesn’t. On the other hand, PATCH makes partial updates to the resource if it exists and does nothing if it doesn’t.
Making a PUT request to an API is similar to making a POST request. The only difference is in the configuration object you pass to Axios, or the HTTP method you need to invoke to make a PUT request to the API.
You can replace the onSubmitFormHandler of the POST request with the code below to make a PUT request. For completeness, I am using Promise chaining rather than async/await in the event handler below:
 const onSubmitFormHandler = (event) => {
    if (!fullName.trim() || !email.trim()) {
      alert("Name or Email is invalid");
      return;
    }
    setIsLoading(true);

    const configurationObject = {
      url: `${baseUrl}/api/users/2`,
      method: "PUT",
      data: { fullName, email },
    };

    axios(configurationObject)
      .then((response) => {
        if (response.status === 200) {
          alert(` You have updated: ${JSON.stringify(response.data)}`);
          setIsLoading(false);
          setFullName("");
          setEmail("");
        } else {
          throw new Error("An error has occurred");
        }
      })
      .catch((error) => {
        alert("An error has occurred");
        setIsLoading(false);
      });
  };

How to make a DELETE request using Axios in React Native

You can make DELETE requests using Axios the same way you make POST and PUT requests.
Just like its name suggests, a DELETE request will delete a resource from the server side. You can replace the onSubmitFormHandler of the code for making a POST request with the event handler below to make a DELETE request. The rest of the code remains the same:
const onSubmitFormHandler = async (event) => {
    if (!fullName.trim() || !email.trim()) {
      alert("Name or Email is invalid");
      return;
    }
    setIsLoading(true);
    try {
      const response = await axios.delete(`${baseUrl}/api/users/2`, {
        fullName,
        email,
      });
      if (response.status === 204) {
        alert(` You have deleted: ${JSON.stringify(response.data)}`);
        setIsLoading(false);
        setFullName('');
        setEmail('');
      } else {
        throw new Error("Failed to delete resource");
      }
    } catch (error) {
      alert("Failed to delete resource");
      setIsLoading(false);
    }
  }; 
Conclusion
Making network requests to an API is inevitable when you are building a mobile application, and Axios is one of the most popular HTTP clients out there. It comes with added functionalities, making networking as simple as possible.
The APIs your app interacts with can be self hosted or third party APIs. For improved user experience, effective management of the network request-response cycle is paramount.
On the flip side, you need to weigh the tradeoffs of adding third party packages like Axios to your mobile application. Though Axios is a popular and well-maintained package, it will increase your bundle size by 367kB according to Packagephobia.
Though that bundle size might look like a small addition to your app when using powerful mobile devices, you need to think about the effect it will have on your users with less powerful mobile devices. In the same vein, you need to ensure third party packages like Axios do not introduce security vulnerabilities to your application.
The post Using Axios with React Native to manage API requests appeared first on LogRocket Blog.