Skip to content

RN Lezione 12: API avanzate, Refresh e POST

  • Aggiungere pull-to-refresh alla FlatList
  • Fare chiamate POST con body JSON
  • Passare parametri a query API
  • Gestire errori di rete in modo robusto

1. Pull-to-refresh — tirare per ricaricare

Section titled “1. Pull-to-refresh — tirare per ricaricare”
const [refreshing, setRefreshing] = useState(false);
const [dati, setDati] = useState([]);
const caricaDati = async () => {
const res = await fetch('https://api.esempio.com/dati');
const json = await res.json();
setDati(json);
};
const onRefresh = async () => {
setRefreshing(true);
await caricaDati(); // ← ricarica i dati
setRefreshing(false);
};
<FlatList
data={dati}
renderItem={renderItem}
refreshing={refreshing} // ← mostra/nasconde indicatore
onRefresh={onRefresh} // ← funzione da chiamare
/>
import { View, Text, FlatList, ActivityIndicator, StyleSheet } from 'react-native';
import { useState, useEffect } from 'react';
export default function App() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [errore, setErrore] = useState(null);
useEffect(() => {
fetchPosts();
}, []);
const fetchPosts = async () => {
try {
const res = await fetch(
'https://jsonplaceholder.typicode.com/posts?_limit=10'
);
if (!res.ok) throw new Error('Errore caricamento');
const data = await res.json();
setPosts(data);
setErrore(null);
} catch (err) {
setErrore(err.message);
} finally {
setLoading(false);
setRefreshing(false);
}
};
const onRefresh = () => {
setRefreshing(true);
fetchPosts();
};
if (loading) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
if (errore && posts.length === 0) {
return (
<View style={styles.center}>
<Text style={styles.erroreTesto}>{errore}</Text>
</View>
);
}
return (
<View style={styles.container}>
<FlatList
data={posts}
renderItem={({ item }) => (
<View style={styles.card}>
<Text style={styles.titolo}>{item.title}</Text>
<Text style={styles.corpo}>{item.body}</Text>
</View>
)}
keyExtractor={item => item.id.toString()}
refreshing={refreshing}
onRefresh={onRefresh}
/>
</View>
);
}

// URL con parametri (stringa)
const citta = 'Roma';
const res = await fetch(
`https://geocoding-api.open-meteo.com/v1/search?name=${citta}&count=1`
);
// URL con parametri (URLSearchParams)
const params = new URLSearchParams({
name: 'Roma',
count: '1',
language: 'it',
format: 'json',
});
const res = await fetch(
`https://geocoding-api.open-meteo.com/v1/search?${params}`
);
const [citta, setCitta] = useState('');
const [risultati, setRisultati] = useState([]);
const [cercando, setCercando] = useState(false);
const cerca = async () => {
if (!citta.trim()) return;
setCercando(true);
try {
const res = await fetch(
`https://geocoding-api.open-meteo.com/v1/search?name=${citta}&count=5`
);
const data = await res.json();
setRisultati(data.results || []);
} catch (err) {
Alert.alert('Errore', err.message);
} finally {
setCercando(false);
}
};

const creaPost = async () => {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: 'Nuovo post',
body: 'Contenuto del post...',
userId: 1,
}),
}
);
const data = await response.json();
console.log('Creato con ID:', data.id);
} catch (err) {
console.error('Errore:', err);
}
};
const [titolo, setTitolo] = useState('');
const [corpo, setCorpo] = useState('');
const [salvataggio, setSalvataggio] = useState(false);
const inviaPost = async () => {
if (!titolo || !corpo) {
Alert.alert('Errore', 'Compila tutti i campi');
return;
}
setSalvataggio(true);
try {
const res = await fetch(
'https://jsonplaceholder.typicode.com/posts',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: titolo, body: corpo, userId: 1 }),
}
);
const data = await res.json();
Alert.alert('✅ Successo', `Post creato con ID ${data.id}`);
setTitolo('');
setCorpo('');
} catch (err) {
Alert.alert('❌ Errore', err.message);
} finally {
setSalvataggio(false);
}
};

const fetchSicura = async (url, options = {}) => {
try {
const response = await fetch(url, options);
// Controllo HTTP error
if (!response.ok) {
throw new Error(
`Errore HTTP ${response.status}: ${response.statusText}`
);
}
const data = await response.json();
return { success: true, data };
} catch (err) {
// Distinguere errori di rete da errori API
if (err.message.includes('Network request failed')) {
return {
success: false,
error: 'Nessuna connessione internet. Controlla la rete.',
};
}
if (err.message.includes('HTTP')) {
return {
success: false,
error: `Errore del server: ${err.message}`,
};
}
return { success: false, error: err.message };
}
};
// Uso
const carica = async () => {
const result = await fetchSicura('https://api.esempio.com/dati');
if (result.success) {
setDati(result.data);
} else {
Alert.alert('Errore', result.error);
}
};

Per app didattiche, puoi mettere la chiave in un file di configurazione:

config.js
export const API_KEY = 'la_tua_chiave_qui';
// App.js
import { API_KEY } from './config';
const res = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=Roma&appid=${API_KEY}`
);

⚠️ Per app reali: mai hardcodare API key! Usa variabili d’ambiente o un backend proxy.


import { View, Text, TextInput, TouchableOpacity, ActivityIndicator, StyleSheet, Alert } from 'react-native';
import { useState, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
const CHIAVE_CITTA = '@ultima_citta';
export default function App() {
const [citta, setCitta] = useState('');
const [meteo, setMeteo] = useState(null);
const [loading, setLoading] = useState(false);
const [errore, setErrore] = useState('');
useEffect(() => {
caricaUltimaCitta();
}, []);
const caricaUltimaCitta = async () => {
const saved = await AsyncStorage.getItem(CHIAVE_CITTA);
if (saved) {
setCitta(saved);
cercaMeteo(saved);
}
};
const cercaMeteo = async (nomeCitta) => {
if (!nomeCitta.trim()) return;
setLoading(true);
setErrore('');
try {
// 1. Geocoding (trova coordinate)
const geoRes = await fetch(
`https://geocoding-api.open-meteo.com/v1/search?name=${nomeCitta}&count=1`
);
const geoData = await geoRes.json();
if (!geoData.results || geoData.results.length === 0) {
throw new Error('Città non trovata');
}
const { latitude, longitude, name } = geoData.results[0];
// 2. Meteo attuale
const meteoRes = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}` +
`&current=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code&timezone=auto`
);
const meteoData = await meteoRes.json();
setMeteo({ citta: name, ...meteoData.current });
await AsyncStorage.setItem(CHIAVE_CITTA, name);
} catch (err) {
setErrore(err.message);
setMeteo(null);
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.titolo}>🌤️ Meteo</Text>
<TextInput
style={styles.input}
placeholder="Nome città..."
value={citta}
onChangeText={setCitta}
onSubmitEditing={() => cercaMeteo(citta)}
/>
<TouchableOpacity
style={styles.bottone}
onPress={() => cercaMeteo(citta)}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="white" />
) : (
<Text style={styles.bottoneTesto}>Cerca</Text>
)}
</TouchableOpacity>
{errore ? (
<View style={styles.cardErrore}>
<Text style={styles.erroreTesto}>{errore}</Text>
</View>
) : null}
{meteo ? (
<View style={styles.cardMeteo}>
<Text style={styles.citta}>{meteo.citta}</Text>
<Text style={styles.temp}>{meteo.temperature_2m}°C</Text>
<Text style={styles.percepita}>
Percepita: {meteo.apparent_temperature}°C
</Text>
<Text style={styles.umidita}>
Umidità: {meteo.relative_humidity_2m}%
</Text>
</View>
) : null}
</View>
);
}

FunzionalitàCodice
Pull-to-refreshrefreshing={s} onRefresh={fn} su FlatList
GET con parametrifetch(`url?${new URLSearchParams(p)}`)
POST datifetch(url, {method:'POST', body: JSON.stringify(d)})
API KeyVariabile in config.js (solo didattica)
Gestione erroritry/catch + !response.ok check

🎯 Esercizio: “App Citazioni con refresh”

Section titled “🎯 Esercizio: “App Citazioni con refresh””

Crea un’app che:

  1. Carica una citazione casuale da https://api.quotable.io/random
  2. Mostra: citazione (grande, corsivo) + autore
  3. Bottone “Nuova citazione” → carica altra
  4. Pull-to-refresh sulla ScrollView
  5. ActivityIndicator durante caricamento
  6. Gestione errore
  7. Salva l’ultima citazione in AsyncStorage
{
"content": "La vita è ciò che accade...",
"author": "John Lennon"
}