Gestion d'état
Quand utiliser useState, useQuery, AsyncStorage, et pourquoi Redux n'est pas nécessaire dans ScaleRocket Mobile.
Gestion d'état
ScaleRocket Mobile utilise une approche simple de gestion d'état : les hooks intégrés de React pour l'état local et les requêtes en temps réel de votre backend pour l'état serveur. Pas besoin de Redux.
Catégories d'état
| Catégorie | Outil | Exemple |
|---|---|---|
| État UI | useState | Champs de formulaire, modal ouvert/fermé, chargement |
| État serveur | useQuery (backend) | Données utilisateur, listes, métriques du tableau de bord |
| Préférences persistantes | AsyncStorage | Thème, onboarding, langue |
| Données sécurisées | SecureStore | Tokens d'authentification |
| État partagé | React Context | Thème, session d'auth, toast |
État local avec useState
Pour l'état qui appartient à un seul composant :
function ProfileForm() {
const [name, setName] = useState("");
const [loading, setLoading] = useState(false);
const [showModal, setShowModal] = useState(false);
return (
<View>
<Input label="Name" value={name} onChangeText={setName} />
<Button title="Save" loading={loading} onPress={handleSave} />
</View>
);
}État serveur
Utilisez les patterns React Query avec Supabase pour la récupération, la mise en cache et le re-fetching :
import { useEffect, useState } from "react";
import { supabase } from "@/lib/supabase";
function useProfile(userId: string) {
const [data, setData] = useState<Profile | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
supabase
.from("profiles")
.select("*")
.eq("id", userId)
.single()
.then(({ data }) => {
setData(data);
setIsLoading(false);
});
}, [userId]);
return { data, isLoading };
}Pour les mises à jour en temps réel, abonnez-vous aux changements :
useEffect(() => {
const subscription = supabase
.channel("profile")
.on("postgres_changes", { event: "*", schema: "public", table: "profiles" }, (payload) => {
setData(payload.new as Profile);
})
.subscribe();
return () => { subscription.unsubscribe(); };
}, []);Les requêtes Convex sont en temps réel par défaut. Les données se mettent à jour automatiquement quand elles changent sur le serveur :
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";
function Dashboard() {
const profile = useQuery(api.users.getProfile);
const metrics = useQuery(api.dashboard.getMetrics);
if (profile === undefined || metrics === undefined) {
return <Loading />;
}
return (
<View>
<Text>Welcome, {profile.name}</Text>
<Text>Revenue: ${metrics.revenue}</Text>
</View>
);
}Les mutations mettent à jour le serveur et toutes les requêtes se relancent automatiquement :
import { useMutation } from "convex/react";
function ProfileForm() {
const updateProfile = useMutation(api.users.updateProfile);
const handleSave = async () => {
await updateProfile({ name: "New Name" });
// Pas besoin de re-fetch — les requêtes se mettent à jour automatiquement
};
}État partagé avec Context
Pour l'état partagé entre plusieurs écrans, utilisez React Context :
// lib/auth-context.tsx
const AuthContext = createContext<AuthContextType | null>(null);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
// ... logique d'auth
return (
<AuthContext.Provider value={{ user, loading, signIn, signOut }}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext)!;AsyncStorage pour les préférences
Pour les données qui doivent persister entre les redémarrages de l'app mais qui ne sont pas sensibles :
import AsyncStorage from "@react-native-async-storage/async-storage";
// Hook personnalisé pour un état persistant
function usePersistentState<T>(key: string, defaultValue: T) {
const [value, setValue] = useState<T>(defaultValue);
useEffect(() => {
AsyncStorage.getItem(key).then((stored) => {
if (stored) setValue(JSON.parse(stored));
});
}, [key]);
const setPersistentValue = (newValue: T) => {
setValue(newValue);
AsyncStorage.setItem(key, JSON.stringify(newValue));
};
return [value, setPersistentValue] as const;
}Pourquoi pas Redux ?
ScaleRocket Mobile évite Redux car :
- L'état serveur est géré par le système de requêtes de votre backend (temps réel Convex ou abonnements Supabase)
- L'état UI est local aux composants avec
useState - L'état partagé utilise React Context (auth, thème)
- L'état persistant utilise AsyncStorage ou SecureStore
Cela couvre 100% des besoins typiques d'une app mobile sans le boilerplate de Redux, actions, reducers et middleware.