Использование Axios с React Native

Это руководство по использованию Axios с React Native для управления запросами API. Чаще всего вам придется отправлять сетевые запросы к API при создании веб-приложения или мобильного приложения. Вы можете отправлять эти сетевые запросы для аутентификации пользователя, обновления ресурса или получения ресурса с вашего собственного сервера или сторонних API. Fetch API пригодится, если вы хотите отправлять запросы API в среде браузера.

React Native также имеет встроенный API-интерфейс Fetch, аналогичный браузерному, специально для взаимодействия с API вашего мобильного приложения. Однако существуют альтернативные библиотеки, такие как Axios, которые вы можете использовать вместо собственного Fetch API.

Кроме того, большинство разработчиков предпочитают Axios встроенному API выборки из-за его изоморфной природы и готового преобразования JSON. В этой статье вы узнаете, как управлять запросами API с помощью Axios в приложении React Native.

Установка Axios

В зависимости от того, какой менеджер пакетов вы используете, введите одну из приведенных ниже команд в окне терминала, чтобы установить Axios:

# axios
npm install axios

# yarn
yarn add axios

# pnpm
pnpm add axios

В этом разделе вы научитесь управлять запросами API с помощью Axios в приложении React Native. Вы будете использовать Axios для выполнения простой операции CRUD (создание, чтение, обновление и удаление).

Работа с ключами API в React Native

Для доступа к большинству сторонних API требуются секретные учетные данные. Хранить секретные ключи API в исходном коде на стороне клиента — не очень хорошая идея. Если вы это сделаете, любой, кто проверит код вашего прилагаемого приложения, получит доступ к вашему закрытому ключу. Один из рекомендуемых способов управления ключом API — создать уровень оркестрации между сторонним API и вашим приложением.

Например, бессерверная функция может безопасно получить доступ к вашему ключу API. Ваше приложение выполнит вызов конечной точки, предоставляемой бессерверной функцией.

Бессерверная функция обеспечит безопасный доступ к вашему ключу API, вызовет сторонний API, получит необходимый вам ресурс и передаст его в ваше мобильное приложение.

Управление состоянием приложения в цикле сетевой запрос-ответ

Когда вы инициируете сетевой запрос к API, запрос либо будет успешным, либо не удастся. Поэтому важно отслеживать различные состояния вашего приложения с момента запроса до получения ответа от сервера. Вы можете отображать индикатор загрузки, пока данные еще находятся в процессе передачи. Если операция CRUD прошла успешно, вы показываете пользователю сообщение об успехе. В случае неудачи вы отобразите соответствующее сообщение об ошибке.

Это важно, поскольку клиент, использующий ваше приложение, может иметь медленный доступ в Интернет или вообще не иметь его. Иногда сервер API может простаивать. Отслеживание состояния вашего приложения и отображение соответствующих сообщений обеспечат хороший UX.

В этой статье мы будем использовать reqres REST API. Это API-заполнитель, состоящий из фиктивных данных. Вы можете экспериментировать с конечными точками, используя инструменты тестирования API, такие как Postman или Insomnia.

Как отправлять запросы с помощью Axios в React Native

GET запрос

В этом разделе мы отправим запрос GET к конечной точке /api/users, чтобы получить пользователя. GET - это HTTP-метод, который вы используете, если хотите запросить ресурс с сервера. Мы сохраняем идентификатор пользователя в состоянии, как показано во фрагменте кода ниже. Вы можете изменить идентификатор пользователя внутри обработчика событий onPress, прикрепленного к кнопке "Создать нового пользователя". Изменение идентификатора пользователя вызовет запрос GET к API внутри useEffectHook.

После запуска сетевого запроса мы выводим на экран индикатор загрузки. Если мы успешно извлекаем данные, мы обновляем состояние и удаляем индикатор загрузки. Если по какой-либо причине нам не удается получить данные, мы останавливаем индикатор загрузки и выводим соответствующее сообщение об ошибке. Мы прерываем сетевой запрос в функции очистки, если пользователь решает закрыть приложение до получения ответа от сервера.

Проверьте возвращаемое значение функции effect в перехватчике useEffect. Вот как выглядит код для этого в App.js компонент:

import axios from "axios";
import React, { useState, useEffect } from "react";
import {
  StyleSheet,
  Text,
  ScrollView,
  View,
  Image,
  Platform,
  TouchableHighlight,
} 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: 200, height: 200, borderRadius: 100 }}
      />
      <Text style={{ textAlign: "center", fontSize: 20 }}>
        {`${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 abortController = new AbortController();
    const url = `${baseUrl}/api/users/${userId}`;

    const fetchUsers = async () => {
      try {
        setIsLoading(true);

        const response = await axios.get(url, {
          signal: abortController.signal,
        });

        if (response.status === 200) {
          setUser(response.data.data);
          setIsLoading(false);

          return;
        } else {
          throw new Error("Failed to fetch users");
        }
      } catch (error) {
        if (abortController.signal.aborted) {
          console.log("Data fetching cancelled");
        } else {
          setErrorFlag(true);
          setIsLoading(false);
        }
      }
    };

    fetchUsers();

    return () => abortController.abort("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>
        <TouchableHighlight
          onPress={changeUserIdHandler}
          disabled={isLoading}
          style={styles.buttonStyles}
        >
          <Text style={styles.textStyles}>Get New User</Text>
        </TouchableHighlight>
      </View>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "white",
    alignItems: "center",
    justifyContent: "center",
    marginTop: Platform.OS === "ios" ? 0 : Constants.statusBarHeight,
  },
  wrapperStyle: {
    minHeight: 128,
  },
  buttonStyles: {
    backgroundColor: "dodgerblue",
  },
  textStyles: {
    fontSize: 20,
    color: "white",
    padding: 10,
  },
});

Приведенный выше код получит и отобразит имя и изображение пользователя на экране. На изображении ниже показано, как это выглядит в эмуляторе Android:

Использование Axios с React Native для создания нового пользователя

POST запрос

В этом разделе вы узнаете, как сделать запрос на публикацию. POST - это HTTP-метод, который вы используете для отправки данных на сервер для обновления или создания ресурса. API-заполнитель, который мы используем, предоставляет конечную точку /api/users для создания ресурса. После успешного создания ресурса вы получите ответ с кодом состояния 201.

Выполнение запроса POST в Axios аналогично выполнению запроса GET. В большинстве случаев POST-запросы выполняются с использованием пользовательских данных, отправленных с помощью формы. Отправленные данные могут быть получены из форм входа, регистрации или форм обратной связи от ваших клиентов. Такие данные требуют проверки на стороне клиента перед отправкой.

Вы можете использовать один из пакетов форм для проверки данных при создании сложных приложений. Большинство пакетов хорошо спроектированы, оптимизированы и имеют большое сообщество. Однако прежде чем интегрировать библиотеку в ваше приложение, изучите возможные компромиссы. Особенно размер дополнительного пакета, который вы добавляете в свое приложение, и потенциальные уязвимости безопасности, которые он может создать.

Существует два основных пакета React для управления формами. Это пакеты Formik и React Hook Form . В приведенном ниже фрагменте кода у нас есть форма React Native для полного имени и адреса электронной почты пользователя.

Оба TextInput компонента являются контролируемыми компонентами. В идеале, когда пользователь заполняет форму, вы выполняете проверку данных в режиме реального времени. Однако в данном случае это не тот случай, поскольку проверка данных формы выходит за рамки этой статьи.

После нажатия кнопки «Отправить»TextInput поля и кнопка «Отправить» отключаются, прежде чем вы отобразите сообщение, показывающее, что вы создаете ресурс. Отключение кнопки отправки гарантирует, что пользователь не отправит несколько заявок. После успешной отправки запроса POST вы отображаете пользователю сообщение об успехе:

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="Your Email"
            placeholderTextColor="#ffffff"
            style={styles.input}
            value={email}
            editable={!isLoading}
            onChangeText={onChangeEmailHandler}
          />
        </View>
        <View>
          <Button
            title="Submit"
            onPress={onSubmitFormHandler}
            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",
    fontSize: 20,
  },
  wrapper: {
    marginBottom: 10,
  },
  input: {
    borderWidth: 2,
    borderColor: "grey",
    minWidth: 200,
    textAlignVertical: "center",
    paddingLeft: 10,
    borderRadius: 20,
    color: "#ffffff",
  },
});

Приведенный выше код отобразит форму с полями ввода имени и адреса электронной почты. Пользователь может заполнить форму и нажать кнопку отправки , чтобы сделать запрос POST. На изображении ниже показано, как это выглядит в эмуляторе Android:

Axios и React Native Создание нового пользователя

На скриншоте выше заголовок формы - Создать нового пользователя. Однако после нажатия кнопки отправки заголовок меняется на Создание ресурса, а кнопка отправки отключена до завершения процесса:

Использование Axios и React Native для создания ресурса

PUT запрос

Для обновления ресурса требуется метод PUT или PATCH, хотя я сосредоточусь на PUT. Если ресурс существует, использование метода PUT полностью перезаписывает его и создает новый ресурс, если его нет. С другой стороны, PATCH частично обновляет ресурс, если он существует, и ничего не делает, если его нет.

Выполнение запроса PUT к API аналогично выполнению запроса POST. Единственная разница заключается в объекте конфигурации, который вы передаете в Axios, или в методе HTTP, который вам нужно вызвать, чтобы отправить запрос PUT к API.

Вы можете заменить onSubmitFormHandler запроса POST приведенным ниже кодом, чтобы сделать запрос PUT. Для полноты картины я использую цепочку Promises, а не async/await в обработчике событий ниже:

 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);
      });
  };

DELETE запрос

Вы можете отправлять запросы DELETE с помощью Axios так же, как вы делаете запросы POST и PUT. Как следует из названия, запрос DELETE удалит ресурс со стороны сервера. Вы можете заменить onSubmitFormHandler код для выполнения запроса POST обработчиком событий ниже, чтобы выполнить запрос DELETE. Остальная часть кода остается прежней:

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);
    }
  }; 

Как обрабатывать ошибки с помощью Axios

Как отмечалось выше, Axios основан на Promises. Когда Promis отклоняется, Axios вызывает обработчик отклонения и передает ему объект ошибки. Содержимое объекта ошибки зависит от ответа сервера. Если объект ошибки содержит это response поле, ответ сервера имеет код состояния HTTP, выходящий за пределы успешного 2xx диапазона. Таким образом, вы можете обновить пользовательский интерфейс, выдав соответствующее сообщение об ошибке.

Если объект ошибки содержит это request поле, клиент сделал запрос, но не получил ответа от сервера. Однако если объект ошибки не имеет response ни request поля, это означает, что при настройке запроса произошла ошибка. Вы можете проверить наличие вышеуказанных условий в обработчике отклонения и предпринять соответствующие действия:

axios
  .get("https://reqres.in/api/users/1")
  .then((response) => {
    console.log(response.data);
  })
  .catch((error) => {
    if (error.response) {
      // Update UI accordingly
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      console.log(error.request);
    } else {
      console.log(`Error message: ${error.message}`);
    }
  });

Приведенный выше код использует цепочку Promises. Вы можете переписать его, используя async/await, следующим образом:

const fetchData = async (url) => {
  try {
    const response = await axios.get(url);
    console.log(response.data);
  } catch (error) {
    if (error.response) {
      // Update UI accordingly
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      console.log(error.request);
    } else {
      console.log(`Error message: ${error.message}`);
    }
  }
};

fetchData("https://reqres.in/api/users/1");

Заключение

Выполнение сетевых запросов к API неизбежно при создании мобильного приложения. Axios — один из самых популярных HTTP-клиентов, обладающий дополнительными функциями, максимально упрощающими работу в сети.