Venant de TanStack Query
Qora partage le même modèle conceptuel que TanStack Query : clés de requête, stale-while-revalidate, refetch en arrière-plan, hooks de cycle de vie des mutations et pause-et-replay hors-ligne. La plupart des concepts se traduisent directement. Cette page met en correspondance ce que vous connaissez déjà avec l'API Qora.
Correspondance des concepts
| Concept TanStack Query | Équivalent Qora |
|---|---|
QueryClient | QoraClient |
QueryClientProvider | QoraScope |
useQuery | QoraBuilder<T> (widget) ou useQuery<T> (hook) |
useMutation | QoraMutationBuilder (widget) ou useMutation (hook) |
useInfiniteQuery | InfiniteQueryBuilder (widget) ou useInfiniteQuery (hook) |
useQueryClient | context.qora / useQueryClient() (hook) |
useIsFetching | useIsFetching() (hook) ou client.fetchingCountStream |
useIsMutating | useIsMutating() (hook) ou client.activeMutations |
QueryObserver | client.watchQuery<T> / InfiniteQueryObserver |
persistQueryClient | PersistQoraClient |
DevTools | qora_devtools_overlay (in-app) |
Référence API : requêtes
Options
| Option TanStack Query | Équivalent Qora | Notes |
|---|---|---|
queryKey | queryKey | Même concept. TanStack utilise des tableaux ; Qora utilise List<dynamic>. Égalité structurelle dans les deux. |
queryFn | fetcher | Même rôle. Renommé. |
enabled | enabled | Identique. |
staleTime | staleTime | Identique. Duration.zero = toujours périmé. |
gcTime (cacheTime en v4) | gcTime | Désormais identique à la v5. |
retry | retryCount | Renommé. Valeur par défaut 3 dans les deux. |
retryDelay | retryDelay / retryDelayCalculator | Backoff exponentiel par défaut. retryDelayCalculator accepte une fonction recevant l'index de tentative. |
refetchOnWindowFocus | refetchOnWindowFocus | Identique. Nécessite FlutterLifecycleManager. |
refetchOnReconnect | refetchOnReconnect | Identique. Nécessite FlutterConnectivityManager. |
refetchOnMount | refetchOnMount | Sémantique identique. null hérite de QoraClientConfig.refetchOnMount. |
refetchInterval | refetchInterval | Identique. Actif tant qu'au moins un abonné est présent. |
networkMode | networkMode | Même nom, mêmes trois valeurs : online, always, offlineFirst. |
select | select (filtre de rebuild) / transform / state.map() | Sémantique différente, voir plus bas. |
placeholderData | placeholderData | Une fonction qui renvoie un placeholder, par exemple depuis une autre requête en cache. |
placeholderData: keepPreviousData | previousData sur Loading / Failure, ou keepPreviousData: true | previousData est toujours présent. keepPreviousData garde l'entrée en Success pendant un refetch. |
initialData | initialData | Injecté avant la première frame. client.hydrateQuery<T>(key, data) fait la même chose de façon impérative. |
initialDataUpdatedAt | initialDataUpdatedAt | Vaut l'epoch par défaut : les données initiales sont périmées et refetchées au montage. |
structuralSharing | structuralSharing | Activé par défaut. Les données imbriquées inchangées gardent leur identité d'un fetch à l'autre. |
notifyOnChangeProps | Non disponible | Toutes les transitions d'état émettent sur le stream. |
Valeurs de retour
| Champ TanStack Query | Équivalent Qora |
|---|---|
status: 'pending' | state is Initial ou state is Loading(previousData: null) |
status: 'error' | state is Failure |
status: 'success' | state is Success |
fetchStatus: 'fetching' | fetchStatus == FetchStatus.fetching |
fetchStatus: 'paused' | fetchStatus == FetchStatus.paused |
fetchStatus: 'idle' | fetchStatus == FetchStatus.idle |
isLoading | state.isLoading |
isFetching | fetchStatus == FetchStatus.fetching |
isError | state.isError |
isSuccess | state.isSuccess |
isPending | state is Initial || state is Loading(previousData: null) |
data | state.dataOrNull ou Success(:final data) via correspondance de motifs |
error | state.errorOrNull ou Failure(:final error) via correspondance de motifs |
dataUpdatedAt | state.updatedAt (extension, non-null uniquement sur Success) |
errorUpdatedAt | Non disponible comme champ séparé |
isFetchedAfterMount | Non disponible |
Référence API : mutations
Options
| Option TanStack Query | Équivalent Qora | Notes |
|---|---|---|
mutationFn | mutator | Renommé. |
onMutate | MutationOptions.onMutate | Même rôle. La valeur de retour devient TContext. |
onSuccess | MutationOptions.onSuccess | Même rôle. Signature : (TData, TVariables, TContext?). |
onError | MutationOptions.onError | Même rôle. Signature : (Object, TVariables, TContext?). |
onSettled | MutationOptions.onSettled | Même rôle. Signature : (TData?, Object?, TVariables, TContext?). |
retry | MutationOptions.retryCount | Renommé. Valeur par défaut 0 (pas de retry). |
retryDelay | MutationOptions.retryDelay | Backoff exponentiel. |
networkMode | MutationOptions.networkMode | Mêmes valeurs. Ajouter offlineQueue: true pour mettre en file les écritures faites hors-ligne et les rejouer à la reconnexion. |
variables | Paramètre de type TVariables | Même concept. |
context (snapshot de rollback) | Paramètre de type TContext | Même concept. Retourné par onMutate, transmis à onError/onSettled. |
Valeurs de retour / state
| Champ TanStack Query | Équivalent Qora |
|---|---|
status: 'idle' | state is MutationIdle |
status: 'pending' | state is MutationPending |
status: 'error' | state is MutationFailure |
status: 'success' | state is MutationSuccess |
isIdle | state.isIdle |
isPending | state.isPending |
isError | state.isError |
isSuccess | state.isSuccess |
data | state.dataOrNull |
error | state.errorOrNull |
variables | state.variablesOrNull |
mutate(variables) | mutate(variables): fire-and-forget, erreurs capturées dans le state |
mutateAsync(variables) | mutateAsync(variables): awaitable, erreurs propagées |
reset() | reset(): transitions vers MutationIdle |
Référence API : QueryClient vers QoraClient
| Méthode TanStack Query | Équivalent Qora |
|---|---|
invalidateQueries({ queryKey }) | client.invalidate(key) |
invalidateQueries(filter) | client.invalidateWhere(predicate) ou client.invalidateQueries(filter) |
setQueryData(queryKey, updater) | client.setQueryData<T>(key, data) |
getQueryData(queryKey) | client.getQueryData<T>(key): retourne T? |
getQueryState(queryKey) | client.getQueryState<T>(key): retourne QoraState<T> |
prefetchQuery | client.prefetch<T>(key, fetcher, options) |
resetQueries | client.removeQuery(key) ou client.clear() |
cancelQueries | Non disponible dans Qora v1 |
isFetching(queryKey) | client.isFetchingCount (compteur global) |
setDefaultOptions | QoraClientConfig.defaultOptions (défini à la construction) |
getDefaultOptions | client.config.defaultOptions |
mount / unmount | Géré par QoraScope / cycle de vie des widgets |
clear() | client.clear() |
Différences clés
Les classes scellées remplacent les chaînes de statut
TanStack Query retourne des objets simples avec des champs status sous forme de chaîne et des raccourcis booléens (isLoading, isError, etc.). Qora retourne une classe scellée QoraState<T>. La correspondance de motifs exhaustive de Dart 3 est appliquée à la compilation, éliminant les branches d'état non gérées :
// TanStack Query (React)
if (isLoading) return <Spinner />;
if (isError) return <Error error={error} />;
return <UserCard user={data} />;
// Qora (Dart 3)
return switch (state) {
Initial() || Loading(previousData: null) => const CircularProgressIndicator(),
Failure(:final error, previousData: null) => ErrorScreen(error),
_ => UserCard(state.dataOrNull!),
};
Le bras _ couvre Success, Loading(previousData != null) et Failure(previousData != null) : tous les cas où les données sont disponibles.
previousData est toujours présent
TanStack Query v5 conserve les données précédentes avec placeholderData: keepPreviousData. Dans Qora, Loading<T> et Failure<T> portent toujours previousData: T? : les données périmées restent accessibles pendant les rafraîchissements en arrière-plan et après une erreur, sans aucune option.
// Afficher les données périmées avec un indicateur pendant un rafraîchissement
case Loading(:final previousData) when previousData != null:
return Stack(children: [
UserCard(previousData),
const LinearProgressIndicator(minHeight: 2),
]);
Si vous préférez que l'entrée reste en Success pendant le refetch, par exemple pour garder un tableau paginé stable, utilisez keepPreviousData: true.
select filtre les rebuilds, il ne transforme pas
Dans TanStack Query, select transforme les données renvoyées au composant. Dans Qora, select sur QoraBuilder et useQuery décide quand le widget se reconstruit : uniquement quand le type d'état ou la valeur sélectionnée change. L'état reçu est toujours l'état complet.
Pour transformer les données, Qora propose deux outils :
// TanStack Query
useQuery({ queryKey, queryFn, select: (data) => data.name });
// Qora : transformer une fois, dans le cache (tous les abonnés voient le résultat)
QoraOptions(transform: (raw) => User.fromJson(raw as Map<String, dynamic>));
// Qora : transformer par consommateur, sur l'état
final nameState = userState.map((user) => user.name);
// QoraState<User> → QoraState<String>, previousData compris
// Qora : reconstruire uniquement quand le nom change
QoraBuilder<User>(
queryKey: ['users', id],
fetcher: () => api.getUser(id),
select: (user) => user.name,
builder: (context, state, _) => Text(state.dataOrNull?.name ?? ''),
);
gcTime remplace cacheTime
Qora utilise le nom de TanStack Query v5. gcTime est la durée pendant laquelle une entrée inactive reste en mémoire après l'annulation de son dernier abonné.
API Widget vs API Hook
L'API principale de React Query est basée sur les hooks (useQuery, useMutation). Qora fournit les deux :
QoraBuilder<T>etQoraMutationBuilderfonctionnent dans toutStatelessWidgetouStatefulWidget.useQuery<T>etuseMutationnécessitentHookWidgetdeflutter_hooks.
Les deux partagent le même cache QoraClient. Si un QoraBuilder a déjà récupéré ['users', id], useQuery l'obtient au premier frame depuis la même entrée.
QoraScope est un StatefulWidget, pas un Provider
TanStack Query utilise QueryClientProvider (contexte React). Qora utilise QoraScope, un StatefulWidget qui expose le client à son sous-arbre via un InheritedWidget interne. Accéder au client depuis n'importe quel descendant via context.qora ou QoraScope.of(context).
La persistance nécessite un enregistrement explicite des sérialiseurs
Les plugins de persistance de TanStack Query (ex. createSyncStoragePersister) utilisent JSON.stringify / JSON.parse sur le cache de requêtes complet. Qora nécessite un enregistrement explicite des sérialiseurs par type car Dart n'a pas de réflexion à l'exécution :
// Requis avant hydrate()
client.registerSerializer<User>(
QoraSerializer(toJson: (u) => u.toJson(), fromJson: User.fromJson),
name: 'User',
);
await client.hydrate();
La chaîne name doit être stable entre les versions de l'application et les modes de build (y compris les builds obfusqués).
Pas d'équivalent React Suspense
TanStack Query supporte suspense: true pour s'intégrer avec React Suspense. Flutter n'a pas de mécanisme équivalent. Tout état asynchrone est géré via la correspondance de motifs sur QoraState<T> dans l'arbre de widgets.
Requêtes infinies
| TanStack Query | Qora |
|---|---|
useInfiniteQuery | InfiniteQueryBuilder / useInfiniteQuery |
queryFn({ pageParam }) | fetcher(TPageParam pageParam) |
initialPageParam | InfiniteQueryOptions.initialPageParam |
getNextPageParam(lastPage, pages) | InfiniteQueryOptions.getNextPageParam(lastPage, pages) |
getPreviousPageParam | InfiniteQueryOptions.getPreviousPageParam |
maxPages | InfiniteQueryOptions.maxPages |
fetchNextPage() | observer.fetchNextPage() |
fetchPreviousPage() | observer.fetchPreviousPage() |
hasNextPage | InfiniteSuccess.hasNextPage |
hasPreviousPage | InfiniteSuccess.hasPreviousPage |
isFetchingNextPage | InfiniteSuccess.isFetchingNextPage |
isFetchingPreviousPage | InfiniteSuccess.isFetchingPreviousPage |
data.pages | InfiniteData.pages |
data.pageParams | InfiniteData.pageParams |
data.pages.flat() | InfiniteData.flatten((page) => page.items) |
queryClient.setQueryData (infini) | client.setInfiniteQueryData<TData, TPageParam>(key, InfiniteData) |
queryClient.getQueryData (infini) | client.getInfiniteQueryData<TData, TPageParam>(key) |
DevTools
Les DevTools de TanStack Query sont un panneau navigateur installé comme composant React. L'équivalent dans Qora est un overlay intégré à l'application : installez qora_devtools_overlay et enveloppez l'application avec QoraInspector. Il montre les requêtes, les mutations et une timeline, et n'affiche rien en build release.
Voir Vue d'ensemble des DevTools pour les instructions de configuration.
Liens connexes
- Qu'est-ce que Qora : vue d'ensemble conceptuelle
- Démarrage rapide : configuration en cinq minutes
- QoraState : référence de la classe scellée
- QoraOptions : référence complète des options par requête
- QoraClientConfig : référence de la configuration globale