Gestion d'état — React Query + State local¶
Primatch utilise une stratégie claire de séparation du state serveur et du state UI.
Principe fondamental¶
State serveur → TanStack React Query (données API, cache)
State UI local → useState / useReducer (modales, formulaires, thème)
State global → React Context (auth, i18n) — à utiliser avec parcimonie
Anti-pattern à éviter
Ne jamais stocker des données API dans un state local (useState) et les synchroniser manuellement. C'est le rôle de React Query.
React Query — Patterns¶
Query (lecture)¶
// hooks/useGame.ts
export const useGame = (id: number) => {
return useQuery({
queryKey: ['games', id], // Clé de cache unique
queryFn: () => gameService.getById(id),
enabled: id > 0, // Query conditionnelle
staleTime: 5 * 60 * 1000, // 5 min avant refetch
});
};
Mutation (écriture)¶
// hooks/useUpdateScore.ts
export const useUpdateScore = (gameId: number) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (score: ScoreDTO) => gameService.submitScore(gameId, score),
// Mutation optimiste : mise à jour immédiate de l'UI
onMutate: async (newScore) => {
await queryClient.cancelQueries({ queryKey: ['games', gameId] });
const previous = queryClient.getQueryData(['games', gameId]);
queryClient.setQueryData(['games', gameId], (old) => ({ ...old, score: newScore }));
return { previous };
},
onError: (_err, _vars, context) => {
// Rollback en cas d'erreur
queryClient.setQueryData(['games', gameId], context?.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['games', gameId] });
},
});
};
Clés de cache — Conventions¶
// Convention : tableau du général au spécifique
['games'] // Liste de toutes les parties
['games', id] // Une partie spécifique
['games', id, 'players'] // Joueurs d'une partie
['user', 'profile'] // Profil utilisateur connecté