Framework
Version

persistQueryClient

Dies ist eine Sammlung von Dienstprogrammen für die Interaktion mit "Persistern", die Ihren QueryClient für die spätere Verwendung speichern. Verschiedene Persister können verwendet werden, um Ihren Client und Cache in vielen verschiedenen Speicherschichten zu speichern.

Persister erstellen

Wie es funktioniert

WICHTIG - Damit die Persistenz ordnungsgemäß funktioniert, möchten Sie wahrscheinlich einen QueryClient mit einem gcTime-Wert übergeben, um den Standardwert während der Hydrierung (wie oben gezeigt) zu überschreiben.

Wenn er bei der Erstellung der QueryClient-Instanz nicht gesetzt ist, wird er bei der Hydrierung standardmäßig auf 300000 (5 Minuten) gesetzt, und der gespeicherte Cache wird nach 5 Minuten Inaktivität verworfen. Dies ist das Standardverhalten der Garbage Collection.

Er sollte auf denselben Wert oder einen höheren Wert als die maxAge-Option von persistQueryClient gesetzt werden. Wenn z. B. maxAge 24 Stunden (Standard) beträgt, dann sollte gcTime 24 Stunden oder länger sein. Wenn er niedriger als maxAge ist, greift die Garbage Collection und verwirft den gespeicherten Cache früher als erwartet.

Sie können ihm auch Infinity übergeben, um das Verhalten der Garbage Collection vollständig zu deaktivieren.

Aufgrund einer JavaScript-Beschränkung beträgt die maximal zulässige gcTime etwa 24 Tage, obwohl es möglich ist, diese Grenze mit timeoutManager.setTimeoutProvider zu umgehen.

tsx
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 60 * 60 * 24, // 24 hours
    },
  },
})
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 60 * 60 * 24, // 24 hours
    },
  },
})

Cache-Busting

Manchmal nehmen Sie Änderungen an Ihrer Anwendung oder Ihren Daten vor, die sofort alle zwischengespeicherten Daten ungültig machen. Wenn dies geschieht, können Sie eine buster-Zeichenfolge als Option übergeben. Wenn der gefundene Cache nicht dieselbe Buster-Zeichenfolge hat, wird er verworfen. Die folgenden Funktionen akzeptieren diese Option

tsx
persistQueryClient({ queryClient, persister, buster: buildHash })
persistQueryClientSave({ queryClient, persister, buster: buildHash })
persistQueryClientRestore({ queryClient, persister, buster: buildHash })
persistQueryClient({ queryClient, persister, buster: buildHash })
persistQueryClientSave({ queryClient, persister, buster: buildHash })
persistQueryClientRestore({ queryClient, persister, buster: buildHash })

Entfernung

Wenn Daten als eine der folgenden gefunden werden

  1. abgelaufen (siehe maxAge)
  2. gebusted (siehe buster)
  3. Fehler (z. B. wirft ...)
  4. leer (z. B. undefined)

wird die removeClient()-Funktion des Persisters aufgerufen und der Cache sofort verworfen.

API

persistQueryClientSave

  • Ihre Abfrage/Mutation wird dehydriert und vom bereitgestellten Persister gespeichert.
  • createSyncStoragePersister und createAsyncStoragePersister drosseln diese Aktion auf maximal einmal pro Sekunde, um potenziell teure Schreibvorgänge zu sparen. Überprüfen Sie deren Dokumentation, um zu sehen, wie Sie ihre Drosselungszeiten anpassen können.

Sie können dies verwenden, um den Cache zu den von Ihnen gewählten Zeitpunkten explizit zu speichern.

tsx
persistQueryClientSave({
  queryClient,
  persister,
  buster = '',
  dehydrateOptions = undefined,
})
persistQueryClientSave({
  queryClient,
  persister,
  buster = '',
  dehydrateOptions = undefined,
})

persistQueryClientSubscribe

Führt persistQueryClientSave aus, wann immer sich der Cache für Ihren queryClient ändert. Zum Beispiel: Sie könnten die subscribe initiieren, wenn sich ein Benutzer anmeldet und "Angemeldet bleiben" wählt.

  • Es gibt eine unsubscribe-Funktion zurück, die Sie verwenden können, um die Überwachung zu beenden und die Aktualisierungen des gespeicherten Caches zu beenden.
  • Wenn Sie den gespeicherten Cache nach dem unsubscribe löschen möchten, können Sie einen neuen buster an persistQueryClientRestore senden, der die removeClient-Funktion des Persisters auslöst und den gespeicherten Cache verwirft.
tsx
persistQueryClientSubscribe({
  queryClient,
  persister,
  buster = '',
  dehydrateOptions = undefined,
})
persistQueryClientSubscribe({
  queryClient,
  persister,
  buster = '',
  dehydrateOptions = undefined,
})

persistQueryClientRestore

  • Versucht, einen zuvor gespeicherten dehydrierten Abfrage-/Mutations-Cache vom Persister zurück in den Abfrage-Cache des übergebenen Abfrageclients zu hydrieren.
  • Wenn ein Cache gefunden wird, der älter als das maxAge (standardmäßig 24 Stunden) ist, wird er verworfen. Diese Zeitplanung kann nach Belieben angepasst werden.

Sie können dies verwenden, um den Cache zu den von Ihnen gewählten Zeitpunkten wiederherzustellen.

tsx
persistQueryClientRestore({
  queryClient,
  persister,
  maxAge = 1000 * 60 * 60 * 24, // 24 hours
  buster = '',
  hydrateOptions = undefined,
})
persistQueryClientRestore({
  queryClient,
  persister,
  maxAge = 1000 * 60 * 60 * 24, // 24 hours
  buster = '',
  hydrateOptions = undefined,
})

persistQueryClient

Nimmt die folgenden Aktionen vor

  1. Stellt sofort jeden gespeicherten Cache wieder her (siehe persistQueryClientRestore)
  2. Abonniert den Abfrage-Cache und gibt die unsubscribe-Funktion zurück (siehe persistQueryClientSubscribe).

Diese Funktionalität ist aus Version 3.x erhalten geblieben.

tsx
persistQueryClient({
  queryClient,
  persister,
  maxAge = 1000 * 60 * 60 * 24, // 24 hours
  buster = '',
  hydrateOptions = undefined,
  dehydrateOptions = undefined,
})
persistQueryClient({
  queryClient,
  persister,
  maxAge = 1000 * 60 * 60 * 24, // 24 hours
  buster = '',
  hydrateOptions = undefined,
  dehydrateOptions = undefined,
})

Optionen

Alle verfügbaren Optionen sind wie folgt

tsx
interface PersistQueryClientOptions {
  /** The QueryClient to persist */
  queryClient: QueryClient
  /** The Persister interface for storing and restoring the cache
   * to/from a persisted location */
  persister: Persister
  /** The max-allowed age of the cache in milliseconds.
   * If a persisted cache is found that is older than this
   * time, it will be **silently** discarded
   * (defaults to 24 hours) */
  maxAge?: number
  /** A unique string that can be used to forcefully
   * invalidate existing caches if they do not share the same buster string */
  buster?: string
  /** The options passed to the hydrate function
   * Not used on `persistQueryClientSave` or `persistQueryClientSubscribe` */
  hydrateOptions?: HydrateOptions
  /** The options passed to the dehydrate function
   * Not used on `persistQueryClientRestore` */
  dehydrateOptions?: DehydrateOptions
}
interface PersistQueryClientOptions {
  /** The QueryClient to persist */
  queryClient: QueryClient
  /** The Persister interface for storing and restoring the cache
   * to/from a persisted location */
  persister: Persister
  /** The max-allowed age of the cache in milliseconds.
   * If a persisted cache is found that is older than this
   * time, it will be **silently** discarded
   * (defaults to 24 hours) */
  maxAge?: number
  /** A unique string that can be used to forcefully
   * invalidate existing caches if they do not share the same buster string */
  buster?: string
  /** The options passed to the hydrate function
   * Not used on `persistQueryClientSave` or `persistQueryClientSubscribe` */
  hydrateOptions?: HydrateOptions
  /** The options passed to the dehydrate function
   * Not used on `persistQueryClientRestore` */
  dehydrateOptions?: DehydrateOptions
}

Es gibt tatsächlich drei verfügbare Schnittstellen

  • PersistedQueryClientSaveOptions wird für persistQueryClientSave und persistQueryClientSubscribe verwendet (verwendet keine hydrateOptions).
  • PersistedQueryClientRestoreOptions wird für persistQueryClientRestore verwendet (verwendet keine dehydrateOptions).
  • PersistQueryClientOptions wird für persistQueryClient verwendet

Verwendung mit React

persistQueryClient versucht, den Cache wiederherzustellen und abonniert automatisch weitere Änderungen, wodurch Ihr Client mit dem bereitgestellten Speicher synchronisiert wird.

Die Wiederherstellung ist jedoch asynchron, da alle Persister von Natur aus asynchron sind. Das bedeutet, dass Sie, wenn Sie Ihre App rendern, während die Wiederherstellung läuft, in Race Conditions geraten könnten, wenn eine Abfrage gleichzeitig gemountet wird und abruft.

Darüber hinaus haben Sie keine Möglichkeit, sich abzumelden, wenn Sie Änderungen außerhalb des Lebenszyklus der React-Komponente abonnieren.

tsx
// 🚨 never unsubscribes from syncing
persistQueryClient({
  queryClient,
  persister: localStoragePersister,
})

// 🚨 happens at the same time as restoring
ReactDOM.createRoot(rootElement).render(<App />)
// 🚨 never unsubscribes from syncing
persistQueryClient({
  queryClient,
  persister: localStoragePersister,
})

// 🚨 happens at the same time as restoring
ReactDOM.createRoot(rootElement).render(<App />)

PersistQueryClientProvider

Für diesen Anwendungsfall können Sie den PersistQueryClientProvider verwenden. Er stellt sicher, dass die Abmeldung und Anmeldung korrekt gemäß dem Lebenszyklus der React-Komponente erfolgt, und er stellt auch sicher, dass Abfragen nicht zu fetchen beginnen, während wir noch wiederherstellen. Abfragen werden zwar weiterhin gerendert, sie werden aber in den fetchingState: 'idle' versetzt, bis die Daten wiederhergestellt sind. Dann werden sie erneut abgerufen, es sei denn, die wiederhergestellten Daten sind *frisch genug*, und initialData wird ebenfalls berücksichtigt. Er kann *anstelle* des normalen QueryClientProvider verwendet werden.

tsx
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 60 * 60 * 24, // 24 hours
    },
  },
})

const persister = createAsyncStoragePersister({
  storage: window.localStorage,
})

ReactDOM.createRoot(rootElement).render(
  <PersistQueryClientProvider
    client={queryClient}
    persistOptions={{ persister }}
  >
    <App />
  </PersistQueryClientProvider>,
)
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 60 * 60 * 24, // 24 hours
    },
  },
})

const persister = createAsyncStoragePersister({
  storage: window.localStorage,
})

ReactDOM.createRoot(rootElement).render(
  <PersistQueryClientProvider
    client={queryClient}
    persistOptions={{ persister }}
  >
    <App />
  </PersistQueryClientProvider>,
)

Props

PersistQueryClientProvider nimmt dieselben Props entgegen wie QueryClientProvider, und zusätzlich

  • persistOptions: PersistQueryClientOptions
  • onSuccess?: () => Promise<unknown> | unknown
    • optional
    • wird aufgerufen, wenn die anfängliche Wiederherstellung abgeschlossen ist
    • kann verwendet werden, um pausierte Mutationen fortzusetzen
    • Wenn ein Promise zurückgegeben wird, wird es abgewartet; bis dahin gilt die Wiederherstellung als laufend
  • onError?: () => Promise<unknown> | unknown
    • optional
    • wird aufgerufen, wenn während der Wiederherstellung ein Fehler auftritt
    • Wenn ein Promise zurückgegeben wird, wird es abgewartet

useIsRestoring

Wenn Sie den PersistQueryClientProvider verwenden, können Sie auch den useIsRestoring Hook zusammen damit verwenden, um zu prüfen, ob eine Wiederherstellung gerade im Gange ist. useQuery und ähnliche Funktionen prüfen dies auch intern, um Race Conditions zwischen der Wiederherstellung und neu gemounteten Abfragen zu vermeiden.

Persister

Persister-Schnittstelle

Persister haben die folgenden Schnittstellen

tsx
export interface Persister {
  persistClient(persistClient: PersistedClient): Promisable<void>
  restoreClient(): Promisable<PersistedClient | undefined>
  removeClient(): Promisable<void>
}
export interface Persister {
  persistClient(persistClient: PersistedClient): Promisable<void>
  restoreClient(): Promisable<PersistedClient | undefined>
  removeClient(): Promisable<void>
}

Persisted Client-Einträge haben die folgende Schnittstelle

tsx
export interface PersistedClient {
  timestamp: number
  buster: string
  clientState: DehydratedState
}
export interface PersistedClient {
  timestamp: number
  buster: string
  clientState: DehydratedState
}

Sie können diese importieren (um einen Persister zu erstellen)

tsx
import {
  PersistedClient,
  Persister,
} from '@tanstack/react-query-persist-client'
import {
  PersistedClient,
  Persister,
} from '@tanstack/react-query-persist-client'

Einen Persister erstellen

Sie können auf jede beliebige Weise persistieren. Hier ist ein Beispiel für die Erstellung eines Indexed DB-Persisters. Im Vergleich zur Web Storage API ist Indexed DB schneller, speichert mehr als 5 MB und erfordert keine Serialisierung. Das bedeutet, dass es JavaScript-native Typen wie Date und File problemlos speichern kann.

tsx
import { get, set, del } from 'idb-keyval'
import {
  PersistedClient,
  Persister,
} from '@tanstack/react-query-persist-client'

/**
 * Creates an Indexed DB persister
 * @see https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
 */
export function createIDBPersister(idbValidKey: IDBValidKey = 'reactQuery') {
  return {
    persistClient: async (client: PersistedClient) => {
      await set(idbValidKey, client)
    },
    restoreClient: async () => {
      return await get<PersistedClient>(idbValidKey)
    },
    removeClient: async () => {
      await del(idbValidKey)
    },
  } satisfies Persister
}
import { get, set, del } from 'idb-keyval'
import {
  PersistedClient,
  Persister,
} from '@tanstack/react-query-persist-client'

/**
 * Creates an Indexed DB persister
 * @see https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
 */
export function createIDBPersister(idbValidKey: IDBValidKey = 'reactQuery') {
  return {
    persistClient: async (client: PersistedClient) => {
      await set(idbValidKey, client)
    },
    restoreClient: async () => {
      return await get<PersistedClient>(idbValidKey)
    },
    removeClient: async () => {
      await del(idbValidKey)
    },
  } satisfies Persister
}