RN Lezione 11: useEffect e chiamate API
Obiettivi
Section titled “Obiettivi”- Capire useEffect per operazioni all’avvio
- Fare chiamate fetch a API pubbliche
- Gestire loading, errore e dati
- Mostrare dati da API in FlatList
1. useEffect — eseguire codice al momento giusto
Section titled “1. useEffect — eseguire codice al momento giusto”useEffect esegue codice in momenti specifici del ciclo di vita del componente.
import { useEffect } from 'react';
// 1. All'avvio (UNA SOLA volta)useEffect(() => { console.log('Componente montato!');}, []); // ← array vuoto = esegui 1 sola volta
// 2. Quando una variabile cambiauseEffect(() => { console.log('Il valore di X è cambiato:', x);}, [x]); // ← esegui ogni volta che x cambia
// 3. Cleanup (componente smontato)useEffect(() => { console.log('Avvio...'); return () => { console.log('Pulizia...'); // ← all'uscita };}, []);Pattern principale: fetch all’avvio
Section titled “Pattern principale: fetch all’avvio”const [dati, setDati] = useState(null);const [loading, setLoading] = useState(true);const [errore, setErrore] = useState(null);
useEffect(() => { const caricaDati = async () => { try { const response = await fetch('https://api.esempio.com/dati'); if (!response.ok) throw new Error('Errore HTTP'); const json = await response.json(); setDati(json); } catch (err) { setErrore(err.message); } finally { setLoading(false); } }; caricaDati();}, []);2. fetch API — chiamate HTTP
Section titled “2. fetch API — chiamate HTTP”// GET — leggere dati (default)const response = await fetch('https://api.esempio.com/posts');const dati = await response.json();
// POST — creare daticonst response = await fetch('https://api.esempio.com/posts', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer token...' // se serve }, body: JSON.stringify({ titolo: 'Nuovo post', corpo: 'Contenuto del post...', }),});const risultato = await response.json();
// Controllare errori HTTPif (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`);}3. Esempio: Lista post da JSONPlaceholder
Section titled “3. Esempio: Lista post da JSONPlaceholder”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 [errore, setErrore] = useState(null);
useEffect(() => { fetchPosts(); }, []);
const fetchPosts = async () => { try { const response = await fetch( 'https://jsonplaceholder.typicode.com/posts?_limit=10' ); if (!response.ok) throw new Error('Errore di rete'); const data = await response.json(); setPosts(data); } catch (err) { setErrore(err.message); } finally { setLoading(false); } };
// --- RENDER CONDIZIONALI --- if (loading) { return ( <View style={styles.center}> <ActivityIndicator size="large" color="#3498db" /> <Text style={{ marginTop: 10, color: 'grey' }}> Caricamento in corso... </Text> </View> ); }
if (errore) { return ( <View style={styles.center}> <Text style={{ color: 'red', fontSize: 18, marginBottom: 10 }}> ❌ Errore </Text> <Text style={{ color: 'grey' }}>{errore}</Text> </View> ); }
return ( <View style={styles.container}> <Text style={styles.titolo}>📰 Posts</Text> <FlatList data={posts} renderItem={({ item }) => ( <View style={styles.card}> <Text style={styles.id}>#{item.id}</Text> <Text style={styles.postTitolo}>{item.title}</Text> <Text style={styles.corpo}>{item.body}</Text> </View> )} keyExtractor={item => item.id.toString()} /> </View> );}
const styles = StyleSheet.create({ center: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f5f5f5', }, container: { flex: 1, paddingTop: 50, paddingHorizontal: 15, backgroundColor: '#f5f5f5', }, titolo: { fontSize: 24, fontWeight: 'bold', marginBottom: 15, }, card: { backgroundColor: 'white', padding: 15, borderRadius: 12, marginBottom: 10, elevation: 1, }, id: { fontSize: 12, color: '#3498db', fontWeight: 'bold', marginBottom: 5, }, postTitolo: { fontSize: 16, fontWeight: 'bold', marginBottom: 5, textTransform: 'capitalize', }, corpo: { fontSize: 14, color: 'grey', lineHeight: 20, },});4. ActivityIndicator — spinner di caricamento
Section titled “4. ActivityIndicator — spinner di caricamento”import { ActivityIndicator } from 'react-native';
// Spinner grande blu<ActivityIndicator size="large" color="#3498db" />
// Spinner piccolo<ActivityIndicator size="small" color="grey" />
// Spinner con sfondo<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(255,255,255,0.8)',}}> <ActivityIndicator size="large" /></View>5. API pubbliche gratuite per esercitazioni
Section titled “5. API pubbliche gratuite per esercitazioni”| API | URL | Cosa restituisce |
|---|---|---|
| JSONPlaceholder | jsonplaceholder.typicode.com/posts | Posts, users, comments |
| JSONPlaceholder users | jsonplaceholder.typicode.com/users | Utenti fittizi |
| Rick & Morty | rickandmortyapi.com/api/character | Personaggi |
| Dog API | dog.ceo/api/breeds/image/random | Immagini cani |
| Open-Meteo | api.open-meteo.com/v1/forecast | Meteo gratuito (no API key!) |
Esempio: Dog API (immagine casuale)
Section titled “Esempio: Dog API (immagine casuale)”useEffect(() => { const caricaCane = async () => { const res = await fetch('https://dog.ceo/api/breeds/image/random'); const data = await res.json(); setImmagine(data.message); // ← URL dell'immagine }; caricaCane();}, []);6. Esempio guidato: Personaggi Rick & Morty
Section titled “6. Esempio guidato: Personaggi Rick & Morty”import { View, Text, Image, FlatList, ActivityIndicator, StyleSheet } from 'react-native';import { useState, useEffect } from 'react';
export default function App() { const [personaggi, setPersonaggi] = useState([]); const [loading, setLoading] = useState(true);
useEffect(() => { const fetchCharacter = async () => { try { const res = await fetch('https://rickandmortyapi.com/api/character'); const data = await res.json(); setPersonaggi(data.results.slice(0, 10)); } catch (err) { console.error(err); } finally { setLoading(false); } }; fetchCharacter(); }, []);
if (loading) { return ( <View style={styles.center}> <ActivityIndicator size="large" color="#00b5cc" /> </View> ); }
return ( <View style={styles.container}> <Text style={styles.titolo}>👽 Rick & Morty</Text> <FlatList data={personaggi} renderItem={({ item }) => ( <View style={styles.card}> <Image source={{ uri: item.image }} style={styles.img} /> <View style={styles.info}> <Text style={styles.nome}>{item.name}</Text> <Text style={styles.specie}>{item.species}</Text> <Text style={styles.stato}> {item.status === 'Alive' ? '🟢' : '🔴'} {item.status} </Text> </View> </View> )} keyExtractor={item => item.id.toString()} /> </View> );}7. Tabella riassuntiva
Section titled “7. Tabella riassuntiva”| Hook/API | Codice | Descrizione |
|---|---|---|
| useEffect base | useEffect(() => {}, []) | Esegue all’avvio |
| useEffect con dip | useEffect(() => {}, [x]) | Esegue quando x cambia |
| fetch GET | await fetch(url) | Legge dati |
| fetch POST | await fetch(url, {method:'POST', body:json}) | Invia dati |
| ActivityIndicator | <ActivityIndicator size="large" /> | Spinner |
| JSON parse | await response.json() | Converte risposta in oggetto |
8. Esercizio autonomo
Section titled “8. Esercizio autonomo”🎯 Esercizio: “Elenco utenti”
Section titled “🎯 Esercizio: “Elenco utenti””Crea un’app che:
- Carica utenti da
https://jsonplaceholder.typicode.com/users - Mostra in FlatList: nome, email, telefono
- ActivityIndicator durante il caricamento
- Gestione errore se la fetch fallisce
- Ogni utente ha un’avatar colorato con l’iniziale
Struttura risposta API
Section titled “Struttura risposta API”{ "id": 1, "name": "Leanne Graham", "email": "Sincere@april.biz", "phone": "1-770-736-8031 x56442"}