логотип PurpleSchool
логотип PurpleSchool

Как работать с внешними API на React Native

Автор

Олег Марков

Введение

Мобильное приложение редко бывает «островом»: большинство современных решений общаются с удалёнными серверами, получают и отправляют данные с помощью API (Application Programming Interface). React Native — мощная платформа для кроссплатформенной мобильной разработки на JavaScript и TypeScript — предоставляет эффективные инструменты для работы с такими внешними API.

В этой статье вы поймёте, как правильно организовать работу с внешними API на React Native, узнаете про основные подходы, разберетесь с инструментами вроде fetch и axios, научитесь обрабатывать ответы и ошибки, а также познакомитесь с рекомендациями, которые помогут избегать типичных подводных камней.

Подходы к работе с API в React Native

Зачем нужны внешние API в приложении

Внешние API позволяют вашему приложению:

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

Взаимодействие с внешними API - неотъемлемая часть разработки большинства современных мобильных приложений. React Native предоставляет различные инструменты для выполнения сетевых запросов и обработки данных, полученных от API. Чтобы эффективно работать с внешними API, необходимо понимать принципы работы HTTP, уметь обрабатывать ошибки и асинхронные операции. Если вы хотите детально разобраться в работе с внешними API на React Native и создавать приложения, которые взаимодействуют с различными сервисами — приходите на наш большой курс React Native и Expo Router. На курсе 184 уроков и 11 упражнений, AI-тренажеры для безлимитной практики с кодом и задачами 24/7, решение задач с живым ревью наставника, еженедельные встречи с менторами.

Асинхронность: что нужно знать

Вся работа с сетевыми запросами — асинхронная. Это означает, что приложение не «замерзает» в ожидании ответа API, а продолжает работать дальше. Поэтому для работы с API в React Native вы будете использовать асинхронные функции, промисы и синтаксис async/await.
Давайте рассмотрим на простом примере:

// Функция использует async/await для асинхронного запроса к серверу
async function fetchData() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    const data = await response.json();
    // Здесь данные можно использовать дальше
    console.log(data);
  } catch (error) {
    console.error(error); // Обработка ошибки сети или запроса
  }
}

Основные инструменты: fetch vs axios

В React Native чаще всего используют две библиотеки для работы с API:

  • Fetch — стандартная встроенная функция JavaScript для HTTP-запросов, поддерживается как в браузере, так и в React Native.
  • Axios — популярная сторонняя библиотека для выполнения HTTP-запросов, обладающая расширенными возможностями.

Использование fetch: базовые примеры

Fetch — встроенный вариант, не требует установки. Вот базовый пример GET-запроса:

useEffect(() => {
  // Выполняется запрос на сервер при монтировании компонента
  fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(response => response.json()) // Преобразуем ответ в JSON
    .then(json => setTodo(json)) // Сохраняем полученные данные в состояние
    .catch(error => console.error(error)); // Обрабатываем возможные ошибки
}, []);

Отправка POST-запроса с fetch

fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST', // Указываем, что это POST
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json', // Сообщаем, что отправляем JSON
  },
  body: JSON.stringify({
    title: 'foo', // Данные, которые отправляются на сервер
    body: 'bar',
    userId: 1,
  }),
})
  .then(response => response.json())
  .then(json => console.log(json));

Здесь важно всегда явно настраивать заголовки, когда отправляете JSON.

Использование axios

Axios берёт на себя часть рутины с парсингом данных и настройкой запросов. Для работы поставьте пакет:

npm install axios

Пример GET-запроса с axios

import axios from 'axios';

useEffect(() => {
  // Здесь axios сразу возвращает уже распарсенный ответ, можно передавать параметры
  axios.get('https://jsonplaceholder.typicode.com/users')
    .then(response => setUsers(response.data))
    .catch(error => console.error(error));
}, []);

Пример POST-запроса с axios

axios.post('https://jsonplaceholder.typicode.com/posts', {
  title: 'foo',
  body: 'bar',
  userId: 1,
})
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

Преимущества axios по сравнению с fetch

  • Автоматический парсинг JSON в response.data
  • Простая обработка ошибок (отдельно можно получить ошибки сервера)
  • Удобная настройка заголовков, таймаутов, перехватчиков и многих других опций

В большинстве реальных проектов axios используют чаще, однако fetch отлично подходит для небольших задач и когда не хочется тянуть сторонние зависимости.

Организация кода для работы с API

Выделяйте слой работы с API

Хорошей практикой является вынос логики запросов в отдельные функции/модули. Это упрощает поддержку и тестирование.

// api/userApi.js

import axios from 'axios';

export const getUsers = () => axios.get('https://jsonplaceholder.typicode.com/users');
export const getUserById = (id) => axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
export const createUser = (user) => axios.post('https://jsonplaceholder.typicode.com/users', user);

А в компоненте:

import { getUsers } from './api/userApi';

// ...в компоненте
useEffect(() => {
  getUsers()
    .then(response => setUsers(response.data))
    .catch(error => console.error(error));
}, []);

Такой подход делает ваш код структурированным и модульным.

Обработка ошибок

Ошибки бывают разного рода: сеть недоступна, сервер вернул ошибку, данные имеют неожиданный формат.
Обрабатывать ошибки — всегда важно! Смотрите, как это реализовать с помощью try/catch и axios:

async function loadUser(id) {
  try {
    const response = await axios.get(`/api/users/${id}`);
    setUser(response.data);
  } catch (error) {
    if (error.response) {
      // Сервер ответил ошибкой
      alert(`Ошибка сервера: ${error.response.status}`);
    } else if (error.request) {
      // Не получили ответ
      alert('Нет ответа от сервера');
    } else {
      // Ошибка при формировании запроса
      alert('Ошибка запроса');
    }
  }
}

Разница между error.response и error.request: первая говорит о том, что сервер что-то ответил, вторая — что ответа не было (например, из-за проблем с сетью).

Использование асинхронных функций и хуков

useEffect: выполнение запроса при монтировании компонента

import React, { useEffect, useState } from 'react';

function App() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/users');
        const usersData = await response.json();
        setUsers(usersData);
      } catch (error) {
        console.error(error);
      }
    }

    fetchData();
  }, []); // Пустой массив — значит, вызов один раз при монтировании
}

useCallback: мемоизация функций запроса

Если вы передаёте обработчики или функции API-запроса в дочерние компоненты, их удобно оборачивать в useCallback, чтобы избежать лишних рендеров:

import { useCallback } from 'react';

const fetchTodo = useCallback(async (id) => {
  try {
    const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
    const todo = await response.json();
    setTodo(todo);
  } catch (error) {
    console.error(error);
  }
}, [id]);

Обработка Loading и Error состояний

При работе с запросами всегда нужно учитывать разные состояния:

  • данные загружаются
  • данные успешно загружены
  • возникла ошибка

Давайте реализуем простой паттерн с состояниями:

const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

useEffect(() => {
  setLoading(true); // Отображаем индикатор загрузки
  fetch('https://jsonplaceholder.typicode.com/users')
    .then((response) => response.json())
    .then((data) => setUsers(data))
    .catch((err) => setError(err))
    .finally(() => setLoading(false)); // Готово: скрываем индикатор
}, []);

В компоненте показываем разные сообщения:

{loading && <ActivityIndicator />}
{error && <Text>Ошибка загрузки</Text>}
{!loading && !error && users.map(user => <Text key={user.id}>{user.name}</Text>)}

Тонкости работы с API в React Native

Ограничения платформы

На мобильных устройствах возможны следующие проблемы:

  • Нестабильное подключение к интернету
  • Ограниченные ресурсы устройства
  • Необходимость указывать разрешения на сетевой доступ (Android/iOS)

Проверьте, что ваше приложение запрашивает необходимые разрешения, а также корректно реагирует на долгие или неудачные запросы (таймауты, повторные попытки).

Безопасность работы с API

  • Никогда не храните секретные ключи, токены, пароли в исходниках приложения
  • Используйте защищённые протоколы (https)
  • По возможности реализуйте дополнительную обработку ошибок, чтобы не раскрывать пользователю детали API

Как кешировать данные

Если вам важно не нагружать API и ускорить работу приложения, вы можете сохранять результаты запросов локально, например через AsyncStorage:

import AsyncStorage from '@react-native-async-storage/async-storage';

// Сохраняем результат запроса в local storage
await AsyncStorage.setItem('users', JSON.stringify(users));

// Читаем данные (при отсутствии интернета, например)
const cachedUsers = await AsyncStorage.getItem('users');

Обработка отмены запросов

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

У fetch отмена реализуется через AbortController:

useEffect(() => {
  const controller = new AbortController();

  fetch('https://jsonplaceholder.typicode.com/users', {
    signal: controller.signal // Добавляем сигнал для отмены
  })
    .then(response => response.json())
    .then(data => setUsers(data))
    .catch(error => {
      if (error.name === 'AbortError') {
        // Запрос был отменен
        return;
      }
      // Обрабатываем другие ошибки
      setError(error);
    });

  return () => controller.abort(); // Отменяем запрос при размонтировании компонента
}, []);

Это защищает ваши компоненты от обновления после анмаунта и утечек памяти.

Популярные паттерны и лучшие практики

Используйте готовые решения для state management и запросов

В средних и крупных приложениях удобно управлять асинхронными запросами и состояниями с помощью библиотек:

  • React Query — мощный инструмент для синхронизации данных с сервером, управления загрузками, кешем, повторными попытками, статусами ошибок
  • Redux Toolkit Query — интегрируется с Redux, похожие возможности по запросам, автоматическое кеширование, мутации
  • SWR — библиотека от Vercel для кеширования и обновления данных

Пример использования React Query

import { useQuery } from '@tanstack/react-query'

function UsersComponent() {
  const { data, error, isLoading } = useQuery(['users'], () =>
    fetch('https://jsonplaceholder.typicode.com/users').then(res => res.json())
  );

  if (isLoading) return <Text>Загрузка...</Text>;
  if (error) return <Text>Ошибка: {error.message}</Text>;

  return (
    <View>
      {data.map(user => <Text key={user.id}>{user.name}</Text>)}
    </View>
  );
}

Здесь все процессы запроса, загрузки и ошибок обрабатываются внутри useQuery.

Обрабатывайте ошибки максимально дружелюбно

Показывайте пользователю понятные ошибки. Не выводите технические детали — лучше подскажите, что делать: «Проверьте интернет-соединение», «Повторить попытку».

Логируйте и анализируйте ошибки

Реально помогает сервис интеграций Sentry и аналогичных систем — это позволит находить и реагировать на непредвиденные сбои.

Тестирование работы приложения с API

Мокаем (заменяем) запросы на тестовые

Для тестирования компонентов, делающих сетевые вызовы, используйте моки, чтобы не зависеть от реального API. Например, через jest:

global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve([{ id: 1, name: 'John' }]),
  })
);

// Теперь ваш компонент будет получать предсказуемые данные для тестов

Использование Mock Service Worker (MSW)

Эта утилита позволяет перехватывать запросы не только в тестах, но и в локальной разработке, чтобы симулировать различные сценарии ответов API.

Заключение

Работа с внешними API — неотъемлемая часть мобильной разработки на React Native. С помощью подходов, описанных в статье: использования fetch и axios для запросов, выделения логики работы с API в отдельные модули, обработки асинхронных состояний и ошибок, применения современных инструментов типа React Query, вы сможете создавать надёжные и отзывчивые приложения. Уделяйте внимание безопасности, производительности и чёткому разделению логики, чтобы упростить дальнейшую поддержку.

Однако, для создания полноценного приложения необходимо освоить множество других технологий и подходов, включая работу с UI, данными и нативными функциями. Курс React Native и Expo Router поможет вам в этом. В первых 3 модулях уже доступно бесплатное содержание — начните погружаться в мир React Native прямо сегодня.

Частозадаваемые технические вопросы по теме

Вопрос 1. Почему мой fetch / axios-запрос не срабатывает на Android, а на iOS всё работает?
Ответ: На Android обязательно настройте разрешения на доступ к интернету. В android/app/src/main/AndroidManifest.xml должно быть <uses-permission android_name="android.permission.INTERNET" />. Без этого приложение не сможет выходить в сеть.

Вопрос 2. Как я могу автоматически повторять запрос, если произошел таймаут или временная ошибка?
Ответ: Для этого используйте axios-retry или React Query с параметром retry. В ручных решениях реализуйте рекурсивный вызов функции по таймеру после неудачи.

Вопрос 3. Как передавать токены авторизации в каждом запросе с axios?
Ответ: Используйте перехватчик (interceptor) axios:
javascript axios.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${token}`; return config; }); Это автоматически вставляет токен во все исходящие запросы.

Вопрос 4. Почему получаю ошибку CORS при обращении к тестовому API?
Ответ: В мобильных приложениях CORS-ограничения возникают редко. Если они всё же проявляются, проблема, скорее всего, в сервере. Используйте правильный endpoint или настройте прокси для локальной разработки.

Вопрос 5. Как лучше всего структурировать работу с несколькими независимыми API в одном приложении?
Ответ: Разбивайте логику каждого API в отдельный модуль/folder, например api/githubApi.js, api/newsApi.js. Создавайте универсальные функции-обёртки для запросов, используйте менеджер состояний для централизованного контроля данных и ошибок.

Стрелочка влевоСохранение данных локально на React Native

Постройте личный план изучения React-native до уровня Middle — бесплатно!

React-native — часть карты развития Mobile

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React-native

Работа со ScrollView в React NativeРабота с видео в React NativeКак реализовать аудиоплеер на React NativeНастройка и использование input и textinput в React NativeИнтеграция видео плеера в приложение на React NativeИспользование выпадающих списков в React NativeСоздание и настройка native module на React NativeРабота с изображениями в React NativeКак создать модальные окна в React NativeОтображение списков данных в React NativeГайд по файловой системе в React NativeИнтеграция камеры в приложение на React NativeСоздание интерактивных кнопок в React Native
Как использовать векторные иконки в React Native5 популярных библиотек UI компонентов React NativeСоздание и использование tabs в React NativeРуководство по стилизации компонентов в React NativeОптимизация переходов между экранами в React NativeАдаптация safe area context на React NativeОбзор библиотек для навигации в React NativeСоздание сложных анимаций (reanimated) на React NativeИспользование библиотеки стилей Paper в React NativeРуководство по navigation в React NativeОптимизация отображения списков в React NativeКак реализовать linking на React NativeГайд по UI-китам для React NativeГде искать elements для приложения на React NativeРабота с цветами в React Native
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React Native и Expo Router

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Основы JavaScript

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий