React Native GraphQL-Integration mit Apollo Client
AI generated
RN
native
React Native · GraphQL · Apollo Client · Mobile
React Native GraphQL-Integration mit Apollo Client
ApolloProvider, InMemoryCache, Optimistic UI und Offline-First

Apollo Client verbindet GraphQL und React Native über einen normalisierenden Cache, typisierte Hooks und eine Persistenzschicht, die Daten über App-Neustarts hinweg verfügbar hält. Wer ApolloClient, InMemoryCache und die Apollo-Hooks richtig konfiguriert, bekommt weniger Netzwerk-Roundtrips, sofortiges UI-Feedback durch Optimistic Updates und eine Anwendung, die auch bei instabiler Mobilfunkverbindung nutzbar bleibt.

17 Min. Lesezeit ApolloClient · InMemoryCache · useQuery · useMutation · fetchMore Expo · React Native 0.74+ · Apollo Client 3.x

1. Warum GraphQL und Apollo Client zu React Native passen

React Native Apps laufen selten in stabilen Netzwerkbedingungen. Wechsel zwischen WLAN und Mobilfunk, U-Bahn-Tunnel, Aufzüge und Auslandsroaming sorgen für schwankende Latenz und gelegentliche Verbindungsabbrüche. Genau in diesem Umfeld zeigt sich der Vorteil von GraphQL gegenüber klassischem REST: Statt für eine verschachtelte Ansicht, etwa ein Nutzerprofil mit Posts und Kommentaren, drei bis vier separate REST-Endpunkte nacheinander abzufragen, liefert eine einzige GraphQL-Query genau die benötigten Felder in einem einzigen Roundtrip. Auf einem Mobilfunknetz mit 150 bis 400 Millisekunden Latenz pro Request macht das den Unterschied zwischen einer Ansicht, die sofort wirkt, und einer, die sichtbar in Schüben nachlädt.

Apollo Client ist die Bibliothek, die diese GraphQL-Anfragen für React Native praktisch nutzbar macht. Sie beschränkt sich nicht auf das reine Absetzen von Queries, sondern bringt einen normalisierenden Cache, deduplizierte In-Flight-Requests und React-Hooks mit, die sich wie native Function-Component-Patterns anfühlen. Wer schon einmal manuell fetch-Aufrufe mit useState und useEffect für Loading- und Error-Zustände verdrahtet hat, merkt schnell, wie viel dieser Code durch useQuery und useMutation überflüssig wird.

Ein weiterer Grund liegt im typisierten Schema. GraphQL-Schemas sind ein verbindlicher Vertrag zwischen Backend und App, und Codegen-Werkzeuge erzeugen daraus TypeScript-Typen für jede Query und Mutation. Ein Tippfehler in einem Feldnamen wird so zur Kompilierzeit sichtbar, nicht erst als kaputte UI in Produktion. Bei React Native, wo App-Store-Reviews und OTA-Rollouts Release-Zyklen verlangsamen können, ist dieses frühe Erkennen von Fehlern besonders wertvoll, weil ein Hotfix nicht innerhalb von Minuten live sein kann wie bei einer Web-App.

2. ApolloClient und ApolloProvider: der Grundaufbau

Der Einstieg in Apollo Client beginnt mit drei Bausteinen: einem HttpLink, der die GraphQL-Endpoint-URL kennt, einer InMemoryCache-Instanz und dem ApolloClient-Konstruktor, der beides zusammenführt. Die ApolloProvider-Komponente wrapt danach die App-Wurzel und stellt den Client per React Context allen darunterliegenden Komponenten zur Verfügung, ohne dass er als Prop durch jede Ebene gereicht werden muss. In einer typischen Expo-App passiert das ganz oben in App.tsx, noch vor dem Navigation-Container.

Für Authentifizierung reicht ein einzelner HttpLink selten aus. Üblich ist eine Link-Kette mit ApolloLink.from, in der ein authLink per setContext das Access-Token aus dem sicheren Storage liest und als Authorization-Header anhängt, bevor die Anfrage den httpLink erreicht. Diese Kette lässt sich beliebig erweitern, etwa um einen errorLink für zentrales Logging oder einen retryLink für automatische Wiederholungen bei transienten Netzwerkfehlern.

Direkt im Setup lohnt sich bereits die Cache-Persistenz mit apollo3-cache-persist. Ein CachePersistor wird mit der InMemoryCache-Instanz und einem AsyncStorage-Wrapper erzeugt, und dessen restore-Methode muss vor dem ersten Rendern abgewartet werden. In Expo lässt sich das gut mit SplashScreen.preventAutoHideAsync kombinieren, damit die App nicht kurz einen leeren Cache-Zustand zeigt, bevor die persistierten Daten geladen sind.


# Install Apollo Client, GraphQL, and cache persistence for React Native
npm install @apollo/client graphql
npm install apollo3-cache-persist @react-native-async-storage/async-storage

# Optional: NetInfo to detect connectivity changes for offline-aware links
npm install @react-native-community/netinfo

// apolloClient.js - ApolloClient + ApolloProvider setup with persisted cache
import { useEffect, useState } from 'react';
import {
  ApolloClient,
  ApolloProvider,
  InMemoryCache,
  HttpLink,
  ApolloLink,
} from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { CachePersistor } from 'apollo3-cache-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';

const httpLink = new HttpLink({
  uri: 'https://api.example.com/graphql',
});

// Auth middleware: attach the access token to every outgoing request
const authLink = setContext(async (_, { headers }) => {
  const token = await SecureStore.getItemAsync('access_token');
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    },
  };
});

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        feedItems: {
          keyArgs: false,
          merge(existing = [], incoming, { args }) {
            // Append rather than overwrite so fetchMore extends the list
            const offset = args?.offset ?? 0;
            const merged = existing.slice(0);
            for (let i = 0; i < incoming.length; i++) {
              merged[offset + i] = incoming[i];
            }
            return merged;
          },
        },
      },
    },
  },
});

export function ApolloAppProvider({ children }) {
  const [client, setClient] = useState(null);

  useEffect(() => {
    async function init() {
      const persistor = new CachePersistor({
        cache,
        storage: AsyncStorage,
        maxSize: false,
        debug: __DEV__,
      });

      // Restore persisted cache before the app renders any query result
      await persistor.restore();

      setClient(
        new ApolloClient({
          link: ApolloLink.from([authLink, httpLink]),
          cache,
          connectToDevTools: __DEV__,
        })
      );
    }
    init();
  }, []);

  if (!client) {
    return null; // keep native splash screen visible until cache is restored
  }

  return <ApolloProvider client={client}>{children}</ApolloProvider>;
}

3. InMemoryCache: Normalisierung und Konfiguration

Der zentrale Mehrwert von Apollo Client gegenüber einem einfachen fetch-Wrapper liegt in der InMemoryCache und ihrer Normalisierung. Jedes Objekt, das ein Feld __typename und ein eindeutiges Identifikationsfeld wie id oder uid mitbringt, wird nicht als verschachtelte Kopie im Antwortbaum gespeichert, sondern als flacher Eintrag unter dem Schlüssel Typename:id abgelegt. Fragen zwei unterschiedliche Screens dasselbe Objekt ab, etwa ein Produkt in einer Liste und dasselbe Produkt auf einer Detailseite, zeigen beide dieselbe Cache-Referenz. Ändert eine Mutation dieses Objekt, aktualisieren sich beide Ansichten automatisch, ohne dass ein manueller Re-Fetch nötig ist.

Diese Normalisierung lässt sich über typePolicies feinjustieren. Objekte ohne Standard-id-Feld brauchen eine eigene keyFields-Definition, etwa eine Kombination aus zwei Feldern als zusammengesetzter Schlüssel. Für Listenfelder, die per Pagination wachsen, definiert eine merge-Funktion, wie neue Daten mit bereits vorhandenen kombiniert werden, statt sie einfach zu überschreiben. Ohne eine solche merge-Funktion würde jeder fetchMore-Aufruf die bisherige Liste durch die neue Seite ersetzen, statt sie zu erweitern.

Auf React Native zahlt sich dieser normalisierte Cache doppelt aus. Erstens reduziert er Netzwerk-Traffic, was bei begrenztem mobilem Datenvolumen direkt spürbar ist. Zweitens ermöglicht er sofortige UI-Darstellung beim Zurück-Navigieren zwischen Screens im React-Navigation-Stack, weil useQuery bei gleicher Query und gleichen Variablen zuerst die gecachten Daten zeigt, bevor im Hintergrund ein Refetch entscheidet, ob sich etwas geändert hat.


// schema.graphql (excerpt) - id and __typename drive cache normalization
type Product {
  id: ID!
  name: String!
  price: Float!
  inStock: Boolean!
}

type ProductConnection {
  edges: [ProductEdge!]!
  pageInfo: PageInfo!
}

type ProductEdge {
  cursor: String!
  node: Product!
}

type PageInfo {
  hasNextPage: Boolean!
  endCursor: String
}

type Query {
  products(first: Int!, after: String): ProductConnection!
}

4. Daten laden mit useQuery: Loading, Error, Data

Der useQuery-Hook ist der häufigste Einstiegspunkt in Apollo Client und führt eine GraphQL-Query beim Mounten der Komponente automatisch aus. Er liefert ein Objekt mit loading, error, data sowie Hilfsfunktionen wie refetch und fetchMore zurück. Das übliche Pattern in React Native-Komponenten ist ein frühes Return für den Loading- und Error-Zustand, bevor der eigentliche Content mit den geladenen Daten gerendert wird. Das hält die JSX lesbar und vermeidet verschachtelte Bedingungen tief im Baum.

Über die options von useQuery lassen sich fetchPolicy, skip und variables steuern. Eine fetchPolicy von cache-and-network zeigt sofort gecachte Daten, löst aber parallel einen Netzwerk-Request aus, während cache-first einen Request nur dann feuert, wenn im Cache noch nichts vorliegt. Auf React Native ist die Kombination mit useFocusEffect aus React Navigation sinnvoll: Kommt ein Nutzer per Zurück-Geste auf einen Screen zurück, kann ein gezielter refetch veraltete Daten aktualisieren, ohne dass die Query beim reinen Wechsel des Tabs erneut komplett neu lädt.

Für Pull-to-refresh-Verhalten, das auf Mobile fast erwartet wird, reicht die RefreshControl-Komponente aus React Native in Kombination mit der refetch-Funktion von useQuery. Ein lokaler refreshing-State steuert die Anzeige des Spinners, während refetch im Hintergrund dieselbe Query mit denselben Variablen erneut ausführt und den Cache aktualisiert, sobald die Antwort eintrifft.


// ProductListScreen.jsx - useQuery with explicit loading/error/data handling
import { FlatList, Text, View, RefreshControl } from 'react-native';
import { useQuery, gql } from '@apollo/client';
import { useState, useCallback } from 'react';

const PRODUCTS_QUERY = gql`
  query Products($first: Int!, $after: String) {
    products(first: $first, after: $after) {
      edges {
        cursor
        node {
          id
          name
          price
          inStock
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
`;

export function ProductListScreen() {
  const [refreshing, setRefreshing] = useState(false);

  const { loading, error, data, refetch } = useQuery(PRODUCTS_QUERY, {
    variables: { first: 20, after: null },
    fetchPolicy: 'cache-and-network',
    notifyOnNetworkStatusChange: true,
  });

  const onRefresh = useCallback(async () => {
    setRefreshing(true);
    await refetch();
    setRefreshing(false);
  }, [refetch]);

  if (loading && !data) {
    return <Text>Loading products...</Text>;
  }

  if (error) {
    return <Text>Could not load products: {error.message}</Text>;
  }

  const products = data.products.edges.map((edge) => edge.node);

  return (
    <FlatList
      data={products}
      keyExtractor={(item) => item.id}
      refreshControl={
        <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
      }
      renderItem={({ item }) => (
        <View style={{ padding: 12 }}>
          <Text style={{ fontWeight: 'bold' }}>{item.name}</Text>
          <Text>{item.inStock ? `$${item.price}` : 'Out of stock'}</Text>
        </View>
      )}
    />
  );
}

5. useLazyQuery: Abfragen gezielt auslösen

Während useQuery eine Query sofort beim Mounten der Komponente feuert, wartet useLazyQuery auf einen expliziten Trigger. Der Hook liefert kein einfaches Objekt zurück, sondern ein Tuple: eine Trigger-Funktion an erster Stelle und ein Ergebnis-Objekt mit loading, data und error an zweiter Stelle. Dieses Muster passt zu Interaktionen, bei denen der Zeitpunkt des Requests vom Nutzer bestimmt wird, nicht vom Rendern der Komponente.

Typische Anwendungsfälle in React Native sind ein Suchfeld mit Debounce, das erst nach einer Tippunterbrechung von einigen hundert Millisekunden eine GraphQL-Query abschickt, oder ein Barcode-Scanner-Screen, der eine Produkt-Query erst nach erfolgreichem Scan auslöst. Auch ein Login-Formular profitiert davon: Die Query zur Profilabfrage soll erst nach erfolgreicher Authentifizierung laufen, nicht schon beim Rendern des Formulars.

Ein wichtiger Unterschied zu useQuery betrifft den Cache: useLazyQuery nutzt denselben normalisierten InMemoryCache und respektiert dieselbe fetchPolicy, ausgelöst wird der Request aber ausschließlich durch den manuellen Aufruf der Trigger-Funktion. Wiederholte Aufrufe mit denselben Variablen greifen wie bei useQuery zuerst auf den Cache zu, bevor ein neuer Netzwerk-Request entschieden wird.

6. Mutationen mit useMutation: Optimistic UI und Cache-Updates

Der useMutation-Hook führt Schreiboperationen aus und liefert eine Trigger-Funktion zusammen mit loading-, error- und data-Zuständen. Der wichtigste Hebel für gefühlte Performance ist die optimisticResponse-Option: Statt auf die Server-Antwort zu warten, aktualisiert Apollo Client den Cache sofort mit dem erwarteten Ergebnis, und die UI zeigt die Änderung, noch bevor ein einziges Byte vom Server zurückkommt. Auf Mobilfunknetzen mit 200 bis 800 Millisekunden Latenz pro Request macht das den Unterschied zwischen einer App, die träge wirkt, und einer, die sich nativ schnell anfühlt.

Für Fälle, in denen reine Normalisierung nicht ausreicht, etwa das Einfügen eines neuen Elements in eine Liste, bietet die update-Funktion Zugriff auf cache.modify. Darüber lässt sich ein bestehendes Cache-Feld gezielt lesen und mit dem neuen Element erweitern, ohne die gesamte Query erneut auszuführen. Das ist besonders bei Listen mit Pagination relevant, wo ein einfaches Refetch teuer wäre und den Scroll-Zustand des Nutzers zerstören würde.

Schlägt die Mutation tatsächlich fehl, entfernt Apollo Client die optimistische Schicht automatisch und stellt den vorherigen Cache-Zustand wieder her. Die UI zeigt kurzzeitig die optimistische Version, korrigiert sich dann aber selbstständig, sobald der echte Fehler ankommt. Anwendungscode sollte diesen Fehlerfall trotzdem explizit behandeln, etwa mit einem Toast, der den Nutzer über das Scheitern informiert, weil ein stiller Rollback ohne Hinweis verwirrend wirkt.


// AddToCartButton.jsx - useMutation with optimistic response and cache.modify
import { Pressable, Text } from 'react-native';
import { useMutation, gql } from '@apollo/client';

const ADD_TO_CART = gql`
  mutation AddToCart($productId: ID!, $quantity: Int!) {
    addToCart(productId: $productId, quantity: $quantity) {
      id
      quantity
      product {
        id
        name
      }
    }
  }
`;

export function AddToCartButton({ product }) {
  const [addToCart, { loading }] = useMutation(ADD_TO_CART, {
    variables: { productId: product.id, quantity: 1 },
    // UI updates immediately, before the server responds
    optimisticResponse: {
      addToCart: {
        __typename: 'CartItem',
        id: `optimistic-${product.id}`,
        quantity: 1,
        product: {
          __typename: 'Product',
          id: product.id,
          name: product.name,
        },
      },
    },
    update(cache, { data }) {
      const newItem = data.addToCart;
      cache.modify({
        fields: {
          cartItems(existingItems = []) {
            const newItemRef = cache.writeFragment({
              data: newItem,
              fragment: gql`
                fragment NewCartItem on CartItem {
                  id
                  quantity
                  product {
                    id
                    name
                  }
                }
              `,
            });
            return [...existingItems, newItemRef];
          },
        },
      });
    },
    onError(error) {
      // Apollo Client already rolled the optimistic layer back automatically
      console.warn('Add to cart failed:', error.message);
    },
  });

  return (
    <Pressable onPress={() => addToCart()} disabled={loading}>
      <Text>{loading ? 'Adding...' : 'Add to cart'}</Text>
    </Pressable>
  );
}

7. Fehlerbehandlung: Netzwerkfehler vs. GraphQL-Fehler

Ein ApolloError-Objekt unterscheidet zwei grundlegend verschiedene Fehlerkategorien, und diese Unterscheidung ist für eine saubere React Native-UI entscheidend. Ein networkError bedeutet, dass die Anfrage den Server gar nicht erreicht oder keine Antwort erhalten hat, etwa bei fehlender Verbindung, einem DNS-Fehler oder einem Timeout. Ein graphQLErrors-Array dagegen bedeutet, dass der Server geantwortet hat, die Anfrage aber inhaltlich abgelehnt wurde, etwa wegen fehlender Berechtigung, einer Validierungsregel oder eines nicht gefundenen Datensatzes.

Auf Mobile ist der networkError-Fall besonders häufig, weil Flugmodus, Funklöcher und U-Bahn-Tunnel zum Alltag gehören. Hier ist eine spezifische UX sinnvoll, etwa eine dezente Snackbar mit "Keine Verbindung" statt einer generischen Fehlermeldung, die den Nutzer verunsichert. graphQLErrors lassen sich dagegen oft feiner behandeln: Ein Fehlercode wie AUTH_NOT_AUTHENTICATED in den extensions eines GraphQL-Fehlers kann automatisch zu einem Redirect auf den Login-Screen führen, während ein Validierungsfehler direkt am betroffenen Formularfeld angezeigt wird.

Statt jede Komponente einzeln mit try-catch abzusichern, lohnt sich ein zentraler errorLink in der Apollo-Link-Kette. Dort lassen sich alle Fehler an einen Logging-Dienst wie Sentry weiterreichen, bevor sie überhaupt eine Komponente erreichen. Ein zusätzlicher retryLink kann transiente Netzwerkfehler automatisch mit exponentiellem Backoff wiederholen, sodass ein kurzer Verbindungsabbruch im U-Bahn-Tunnel gar nicht erst als sichtbarer Fehler in der UI ankommt, sondern der zweite oder dritte Versuch bereits erfolgreich ist.

8. Pagination mit fetchMore

Die fetchMore-Funktion, die useQuery zurückgibt, lädt zusätzliche Daten mit veränderten Variablen nach, etwa einem neuen after-Cursor, und kombiniert das Ergebnis über die im Cache definierte merge-Funktion mit den bereits geladenen Daten. In aktuellen Versionen von Apollo Client ist das die empfohlene Vorgehensweise, während die früher genutzte updateQuery-Option inzwischen als veraltet gilt zugunsten der zentralen merge-Logik in den typePolicies.

Für die Struktur der Pagination selbst empfiehlt sich das Cursor-basierte Relay-Connection-Pattern mit edges, node, pageInfo, hasNextPage und endCursor, wie es im Schema-Beispiel aus Abschnitt 3 bereits angelegt ist. Cursor-basierte Pagination ist robuster als reine offset-basierte Pagination, weil sie auch dann konsistent bleibt, wenn sich die zugrunde liegende Liste zwischen zwei Requests ändert, etwa weil ein Element gelöscht wurde.

In der UI löst ein onEndReached-Callback von FlatList den nächsten fetchMore-Aufruf aus, sobald der Nutzer sich dem Ende der aktuell geladenen Liste nähert. Wichtig ist ein Guard gegen doppelte Aufrufe: Ohne eine Prüfung des aktuellen loading-Zustands kann schnelles Scrollen mehrere fetchMore-Aufrufe gleichzeitig auslösen und unnötige Duplikat-Requests erzeugen. Ein einfacher Aufruf sieht so aus: fetchMore(variables: after data.products.pageInfo.endCursor), kombiniert mit einem Footer-Loading-Indikator, solange die nächste Seite lädt.

9. Offline-First auf Mobile und Apollo Client im Vergleich

Mobile Realität bedeutet, dass eine Netzwerkverbindung jederzeit abbrechen kann, während eine App gleichzeitig zwischen Vordergrund und Hintergrund wechselt, wenn der Nutzer zu einer anderen App wechselt oder das Gerät sperrt. Apollo Client ist von Haus aus kein vollständiges Offline-Queue-System, aber die Kombination aus Cache-Persistenz, sauberer Fehlerbehandlung und AppState-Handling deckt die meisten praktischen Fälle einer React Native-App ab.

Der bereits gezeigte CachePersistor sorgt dafür, dass beim App-Start sofort gecachte Daten aus AsyncStorage verfügbar sind, noch bevor eine einzige Netzwerkanfrage beantwortet wurde. Ergänzend lohnt sich ein AppState-Listener, der beim Wechsel in den Zustand active die Funktion client.reFetchObservableQueries aufruft, damit nach einer längeren Zeit im Hintergrund veraltete Daten aktualisiert werden. In Kombination mit @react-native-community/netinfo lässt sich außerdem ein Link konfigurieren, der Mutationen bei fehlender Verbindung zurückhält, statt sie sofort scheitern zu lassen.

Im Vergleich zu anderen Ansätzen für GraphQL und Datenzugriff auf Mobile zeigt sich, warum Apollo Client für komplexere Apps oft die erste Wahl ist, während leichtere Alternativen für einfachere Anforderungen ausreichen können.

Ansatz Cache-Normalisierung Bundle-Größe Empfehlung für React Native
Apollo Client Vollständig, normalisiert nach __typename+id Größer, dafür komplettes Ökosystem Komplexe Apps mit vielen verknüpften Entitäten
urql Optional, über Graphcache-Erweiterung Deutlich kleiner als Apollo Client Kleinere Apps, wenn volle Normalisierung nicht nötig ist
REST + fetch Kein eingebauter Cache Minimal, keine zusätzliche Bibliothek Nur bei sehr einfachen, wenigen Endpunkten sinnvoll
TanStack Query Query-Key-basiert, kein GraphQL-Objekt-Normalisieren Klein, datenquellen-agnostisch Gut für REST, für GraphQL ohne Normalisierung schwächer

10. Zusammenfassung

Die Kombination aus GraphQL und Apollo Client löst für React Native-Apps ein sehr konkretes Problem: verschachtelte Daten in einem einzigen Roundtrip laden, statt mehrere REST-Endpunkte nacheinander abzufragen. ApolloClient und ApolloProvider bilden das Grundgerüst, InMemoryCache normalisiert Objekte über __typename und id, und apollo3-cache-persist hält diesen Cache über App-Neustarts hinweg in AsyncStorage verfügbar. useQuery, useLazyQuery und useMutation decken die drei häufigsten Interaktionsmuster ab: automatisches Laden, nutzergesteuertes Laden und Schreiboperationen mit sofortigem UI-Feedback durch Optimistic Responses.

Für eine robuste mobile Erfahrung zählen zusätzlich die Details: die klare Trennung zwischen networkError und graphQLErrors für passende Fehlermeldungen, cursor-basierte Pagination mit fetchMore für wachsende Listen, und ein AppState-Listener, der beim Zurückkehren aus dem Hintergrund veraltete Daten aktualisiert. Wer diese Bausteine konsequent zusammensetzt, bekommt mit Apollo Client eine React Native-App, die sich auch bei schwankender Mobilfunkverbindung zuverlässig und schnell anfühlt.

React Native GraphQL-Integration mit Apollo Client, das Wichtigste auf einen Blick

Setup

ApolloClient mit HttpLink und InMemoryCache, ApolloProvider wrapt die App-Wurzel, authLink hängt Tokens per setContext an jeden Request.

Cache und Persistenz

InMemoryCache normalisiert über __typename+id, apollo3-cache-persist sichert den Cache in AsyncStorage über App-Neustarts hinweg.

Hooks

useQuery lädt automatisch, useLazyQuery auf Trigger, useMutation mit optimisticResponse für sofortiges UI-Feedback.

Mobile Robustheit

networkError vs. graphQLErrors sauber trennen, fetchMore für Pagination, AppState-Listener für Refetch nach Hintergrund-Phasen.

11. FAQ: React Native GraphQL-Integration mit Apollo Client

1Warum Apollo Client statt fetch?
Normalisierender Cache, deduplizierte Requests und Hooks wie useQuery/useMutation ersparen manuell verdrahtete Loading/Error/Data-Zustände.
2Wie normalisiert InMemoryCache Daten?
Jedes Objekt mit __typename+id landet flach unter Typename:id im Cache, mehrere Komponenten teilen sich dieselbe Referenz.
3Wie persistiert man den Cache?
apollo3-cache-persist mit AsyncStorage, CachePersistor.restore() vor dem ersten Rendern abwarten.
4useQuery vs. useLazyQuery?
useQuery lädt automatisch beim Mounten, useLazyQuery liefert eine Trigger-Funktion für nutzergesteuertes Laden.
5Wie funktioniert Optimistic UI?
optimisticResponse zeigt das erwartete Ergebnis sofort, Apollo Client macht es bei einem Fehler automatisch rückgängig.
6networkError vs. graphQLErrors?
networkError erreicht den Server gar nicht, graphQLErrors sind inhaltliche Ablehnungen einer erfolgreich beantworteten Anfrage.
7Wie funktioniert fetchMore-Pagination?
fetchMore ruft dieselbe Query mit neuem Cursor auf, eine merge-Funktion hängt die neuen Daten an die bestehende Liste an.
8Ist Apollo Client offline-fähig?
Nicht vollständig, aber Cache-Persistenz plus AppState-Refetch decken die meisten mobilen Offline-Szenarien ab.
9Wann Apollo Client statt urql/TanStack Query?
Bei komplexen Apps mit vielen verknüpften Entitäten und Bedarf an vollständiger Cache-Normalisierung.
10Refetch nach Rückkehr aus dem Hintergrund?
Ein AppState-Listener ruft bei active-Wechsel client.reFetchObservableQueries auf, um alle aktiven Queries zu aktualisieren.