Kurs React Native · Moduł 4: Stan i dane z API
Fetch API i Axios - Połączenie z Serwerem
W tej lekcji4
Czas nauczyć się komunikacji z zewnętrznymi API - serwerami Atlantydy!
Fetch API - wbudowane rozwiązanie
import { useState, useEffect } from 'react';
interface Creature {
id: string;
name: string;
species: string;
habitat: string;
}
function CreatureList() {
const [creatures, setCreatures] = useState<Creature[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetchCreatures();
}, []);
const fetchCreatures = async () => {
try {
setIsLoading(true);
const response = await fetch('https://api.atlantyda.app/creatures');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setCreatures(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Wystąpił błąd');
} finally {
setIsLoading(false);
}
};
// POST request
const createCreature = async (creature: Omit<Creature, 'id'>) => {
try {
const response = await fetch('https://api.atlantyda.app/creatures', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123',
},
body: JSON.stringify(creature),
});
if (!response.ok) {
throw new Error('Nie udało się utworzyć stworzenia');
}
const newCreature = await response.json();
setCreatures(prev => [...prev, newCreature]);
} catch (err) {
setError(err instanceof Error ? err.message : 'Błąd');
}
};
if (isLoading) return <ActivityIndicator size="large" />;
if (error) return <Text style={styles.error}>{error}</Text>;
return (
<FlatList
data={creatures}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={styles.card}>
<Text style={styles.name}>{item.name}</Text>
<Text>{item.species}</Text>
</View>
)}
/>
);
}Axios - rozbudowana biblioteka
npx expo install axios// api/client.ts
import axios from 'axios';
import AsyncStorage from '@react-native-async-storage/async-storage';
// Konfiguracja instancji
const apiClient = axios.create({
baseURL: 'https://api.atlantyda.app',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// Interceptor - dodawanie tokenu do każdego requestu
apiClient.interceptors.request.use(
async (config) => {
const token = await AsyncStorage.getItem('authToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Interceptor - obsługa błędów
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Token wygasł - wyloguj użytkownika
await AsyncStorage.removeItem('authToken');
// Przekieruj do logowania
}
return Promise.reject(error);
}
);
export default apiClient;API Service
// api/creatureService.ts
import apiClient from './client';
export interface Creature {
id: string;
name: string;
species: string;
habitat: string;
}
export const creatureService = {
getAll: async (): Promise<Creature[]> => {
const response = await apiClient.get('/creatures');
return response.data;
},
getById: async (id: string): Promise<Creature> => {
const response = await apiClient.get(`/creatures/${id}`);
return response.data;
},
create: async (creature: Omit<Creature, 'id'>): Promise<Creature> => {
const response = await apiClient.post('/creatures', creature);
return response.data;
},
update: async (id: string, updates: Partial<Creature>): Promise<Creature> => {
const response = await apiClient.patch(`/creatures/${id}`, updates);
return response.data;
},
delete: async (id: string): Promise<void> => {
await apiClient.delete(`/creatures/${id}`);
},
search: async (query: string): Promise<Creature[]> => {
const response = await apiClient.get('/creatures/search', {
params: { q: query },
});
return response.data;
},
};Użycie w komponencie
import { creatureService } from '@/api/creatureService';
function CreatureScreen() {
const [creatures, setCreatures] = useState<Creature[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
loadCreatures();
}, []);
const loadCreatures = async () => {
try {
setIsLoading(true);
const data = await creatureService.getAll();
setCreatures(data);
} catch (error) {
if (axios.isAxiosError(error)) {
console.error('Axios error:', error.response?.data);
}
} finally {
setIsLoading(false);
}
};
const handleCreate = async () => {
try {
const newCreature = await creatureService.create({
name: 'Nowy Trytan',
species: 'Tryton',
habitat: 'Głębiny',
});
setCreatures(prev => [...prev, newCreature]);
} catch (error) {
console.error('Create error:', error);
}
};
return (
// ... JSX
);
}Axios oferuje więcej funkcji niż fetch: interceptory, automatyczne transformacje, anulowanie requestów i lepszą obsługę błędów.
Kod do tej lekcji: api/client.ts
import axios from 'axios';
// Konfiguracja instancji axios
const apiClient = axios.create({
baseURL: 'https://api.atlantyda.app',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// Symulacja pobierania tokenu (normalnie z AsyncStorage)
async function getAuthToken(): Promise<string | null> {
return 'token-atlantyda-123';
}
// TODO: dodaj interceptor request, który:
// - pobiera token przez getAuthToken()
// - jeśli token istnieje, ustawia config.headers.Authorization = `Bearer ${token}`
// - zwraca config
apiClient.interceptors.request.use(
async (config) => {
const token = await getAuthToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// TODO: dodaj interceptor response, który przy statusie 401
// wypisze ostrzeżenie o wygaśnięciu tokenu
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
console.warn('Token wygasł - wyloguj użytkownika');
}
return Promise.reject(error);
}
);
export default apiClient;Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Jak poprawnie pobrać dane JSON za pomocą fetch?
2. Która zaleta NIE dotyczy axios w porównaniu do fetch?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Układanie w pionie
Uporządkuj elementy fetch POST od zewnętrznych do wewnętrznych:
- Edytor kodu
Uzupełnij kod apiClient z Bearer token.