Cours React Native · Module 4: État et données d'API
useState et useReducer - Le coffre-fort de l'état
Dans cette leçon3
Bienvenue dans le coffre-fort de l'Atlantide ! C'est ici que nous stockons les données les plus précieuses de notre application. Commençons par les hooks de base pour gérer l'état.
useState - Le coffre-fort basique
import { useState } from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';
function TreasureCounter() {
// État numérique simple
const [treasures, setTreasures] = useState(0);
// État de type objet
const [treasure, setTreasure] = useState({
gold: 0,
pearls: 0,
gems: 0,
});
// État de type tableau
const [collectedItems, setCollectedItems] = useState<string[]>([]);
// Mettre à jour une valeur unique
const addTreasure = () => {
setTreasures(prev => prev + 1);
};
// Mettre à jour un objet (immutabilité !)
const addGold = () => {
setTreasure(prev => ({
...prev,
gold: prev.gold + 10,
}));
};
// Ajouter à un tableau
const collectItem = (item: string) => {
setCollectedItems(prev => [...prev, item]);
};
// Retirer d'un tableau
const removeItem = (index: number) => {
setCollectedItems(prev => prev.filter((_, i) => i !== index));
};
return (
<View style={styles.container}>
<Text style={styles.title}>Coffre-fort : {treasures} trésors</Text>
<Pressable style={styles.button} onPress={addTreasure}>
<Text style={styles.buttonText}>Ajouter un trésor</Text>
</Pressable>
<Text>Or : {treasure.gold}</Text>
<Pressable onPress={addGold}>
<Text>Récolter de l'or</Text>
</Pressable>
<Text>Objets : {collectedItems.join(', ')}</Text>
<Pressable onPress={() => collectItem('Perle')}>
<Text>Récolter une perle</Text>
</Pressable>
</View>
);
}Initialisation paresseuse de l'état
// Pour les opérations d'initialisation coûteuses
const [data, setData] = useState(() => {
// Cette fonction ne s'exécute qu'une seule fois, au premier rendu
return expensiveComputation();
});
// Exemple : chargement depuis AsyncStorage
const [settings, setSettings] = useState<Settings | null>(null);
useEffect(() => {
const loadSettings = async () => {
const saved = await AsyncStorage.getItem('settings');
if (saved) {
setSettings(JSON.parse(saved));
}
};
loadSettings();
}, []);useReducer - Pour un état complexe
import { useReducer } from 'react';
// Types
interface TreasureState {
gold: number;
pearls: number;
gems: number;
items: string[];
isLoading: boolean;
error: string | null;
}
type TreasureAction =
| { type: 'ADD_GOLD'; amount: number }
| { type: 'ADD_PEARL' }
| { type: 'ADD_GEM'; count: number }
| { type: 'COLLECT_ITEM'; item: string }
| { type: 'SET_LOADING'; isLoading: boolean }
| { type: 'SET_ERROR'; error: string }
| { type: 'RESET' };
// Reducer
function treasureReducer(state: TreasureState, action: TreasureAction): TreasureState {
switch (action.type) {
case 'ADD_GOLD':
return { ...state, gold: state.gold + action.amount };
case 'ADD_PEARL':
return { ...state, pearls: state.pearls + 1 };
case 'ADD_GEM':
return { ...state, gems: state.gems + action.count };
case 'COLLECT_ITEM':
return { ...state, items: [...state.items, action.item] };
case 'SET_LOADING':
return { ...state, isLoading: action.isLoading };
case 'SET_ERROR':
return { ...state, error: action.error, isLoading: false };
case 'RESET':
return initialState;
default:
return state;
}
}
// État initial
const initialState: TreasureState = {
gold: 0,
pearls: 0,
gems: 0,
items: [],
isLoading: false,
error: null,
};
// Composant
function TreasureVault() {
const [state, dispatch] = useReducer(treasureReducer, initialState);
return (
<View>
<Text>Or : {state.gold}</Text>
<Text>Perles : {state.pearls}</Text>
<Pressable onPress={() => dispatch({ type: 'ADD_GOLD', amount: 100 })}>
<Text>Ajouter 100 d'or</Text>
</Pressable>
<Pressable onPress={() => dispatch({ type: 'ADD_PEARL' })}>
<Text>Ajouter une perle</Text>
</Pressable>
<Pressable onPress={() => dispatch({ type: 'RESET' })}>
<Text>Réinitialiser le coffre</Text>
</Pressable>
</View>
);
}useState est parfait pour un état simple, useReducer pour une logique complexe comportant de nombreuses actions.
Code de cette leçon : App.tsx
import React, { useState } from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';
export default function App() {
// TODO : crée l'état du compteur de trésors avec une valeur initiale de 0
// const [treasures, setTreasures] = useState(0);
const addTreasure = () => {
// TODO : augmente le compteur de 1 à l'aide de la fonction de mise à jour
// setTreasures(prev => prev + 1);
};
const removeTreasure = () => {
// TODO : diminue le compteur de 1, mais ne descends jamais en dessous de 0
// setTreasures(prev => (prev > 0 ? prev - 1 : 0));
};
return (
<View style={styles.container}>
{/* TODO : affiche le nombre actuel de trésors à la place de 0 */}
<Text style={styles.title}>Trésorerie : 0 trésor</Text>
<View style={styles.row}>
<Pressable style={styles.button} onPress={removeTreasure}>
<Text style={styles.buttonText}>-</Text>
</Pressable>
<Pressable style={styles.button} onPress={addTreasure}>
<Text style={styles.buttonText}>+</Text>
</Pressable>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#1a5276',
},
title: {
fontSize: 22,
color: '#fff',
marginBottom: 24,
},
row: {
flexDirection: 'row',
gap: 16,
},
button: {
paddingVertical: 14,
paddingHorizontal: 28,
borderRadius: 8,
backgroundColor: '#2980b9',
},
buttonText: {
color: '#fff',
fontSize: 20,
fontWeight: '600',
},
});Entrées de la documentation utilisées dans cette leçon
Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Comment initialise-t-on correctement useState avec une valeur initiale ?
2. Comment met-on correctement à jour un seul champ d'un état de type objet ?
Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Tri horizontal
Reconstitue la mise à jour d'état correcte en préservant l'immutabilité :
- Clique dans l'ordre
Clique sur les éléments qui créent une action pour useReducer :
- Éditeur de code
Complète le code du compteur avec les fonctions d'ajout et de retrait.