Fetch и axios в мобильном контексте
Fetch встроен
const res = await fetch('https://api.example.com/users');
const data = await res.json();
Работает так же, как в вебе. Но есть особенности мобильной платформы.
Axios
npm install axios
import axios from 'axios';
const { data } = await axios.get('/users');
Плюсы:
- Автоматический JSON
- Интерцепторы
- Таймауты
- Отмена запросов
- Прогресс загрузки
Базовая настройка
// lib/api.ts
import axios from 'axios';
import * as SecureStore from 'expo-secure-store';
const api = axios.create({
baseURL: process.env.EXPO_PUBLIC_API_URL,
timeout: 10000,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
api.interceptors.request.use(async (config) => {
const token = await SecureStore.getItemAsync('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
api.interceptors.response.use(
(res) => res,
async (error) => {
if (error.response?.status === 401) {
await SecureStore.deleteItemAsync('token');
// навигация на логин
}
return Promise.reject(error);
}
);
export default api;
Переменные окружения
В Expo — EXPO_PUBLIC_*:
EXPO_PUBLIC_API_URL=https://api.example.com
const url = process.env.EXPO_PUBLIC_API_URL;
Отмена запросов
При уходе с экрана:
useEffect(() => {
const controller = new AbortController();
async function load() {
try {
const res = await fetch(url, { signal: controller.signal });
setData(await res.json());
} catch (e) {
if ((e as Error).name !== 'AbortError') setError(e as Error);
}
}
load();
return () => controller.abort();
}, [url]);
С axios:
const source = axios.CancelToken.source();
axios.get(url, { cancelToken: source.token });
return () => source.cancel();
Или AbortController (axios 1.x).
Если ты уже знаешь React
Fetch и axios работают одинаково. Отличия: EXPO_PUBLIC_* для env, SecureStore для токенов, AbortController обязателен.
Таймаут
У fetch нет таймаута. Решение:
async function fetchWithTimeout(url: string, ms: number) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), ms);
try {
const res = await fetch(url, { signal: controller.signal });
return res;
} finally {
clearTimeout(timeout);
}
}
Retry
async function retry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (e) {
if (i === attempts - 1) throw e;
await new Promise((r) => setTimeout(r, 1000 * (i + 1)));
}
}
throw new Error('unreachable');
}
Итоги
- Fetch встроен
- Axios удобнее для больших проектов
- Interceptors для токенов и ошибок
EXPO_PUBLIC_*для envAbortControllerобязателенSecureStoreдля токенов