new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

TanStack Query · React · all subjects

ssr/hydration

34 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

createSyncStoragePersister example with localStorage

Example showing basic setup: ```tsx import { persistQueryClient } from '@tanstack/react-query-persist-client' import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' const queryClient = new QueryClient({ defaultOptions: { queries: { gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) const localStoragePersister = createSyncStoragePersister({ storage: window.localStorage, }) persistQueryClient({ queryClient, persister: localStoragePersister, }) ```

PersistRetryer function signature

The PersistRetryer is a function that receives an object with persistedClient (the PersistedClient that failed to save), error (the Error that occurred), and errorCount (number of retry attempts). It must return a new PersistedClient to retry persistence, or undefined to stop retrying.

removeOldestQuery retry strategy

The removeOldestQuery is a predefined retry strategy that can be imported from '@tanstack/react-query-persist-client'. It returns a new PersistedClient with the oldest query removed, allowing persistence to complete when storage space is exceeded.

createSyncStoragePersister options interface

CreateSyncStoragePersisterOptions interface has the following properties: - storage: Storage | undefined | null (required) - The storage client used for setting and retrieving items from cache (window.localStorage or window.sessionStorage) - key?: string (optional) - The key to use when storing the cache, defaults to 'REACT_QUERY_OFFLINE_CACHE' - throttleTime?: number (optional) - Time in ms to throttle saving the cache to disk, defaults to 1000 - serialize?: (client: PersistedClient) => string (optional) - How to serialize the data to storage, defaults to JSON.stringify - deserialize?: (cachedString: string) => PersistedClient (optional) - How to deserialize the data from storage, defaults to JSON.parse - retry?: PersistRetryer (optional) - How to retry persistence on error, defaults to no retry

Using lz-string compression with createSyncStoragePersister

Example using lz-string for compression to store more data in localStorage: ```tsx import { QueryClient } from '@tanstack/react-query' import { persistQueryClient } from '@tanstack/react-query-persist-client' import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' import { compress, decompress } from 'lz-string' const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: Infinity } }, }) persistQueryClient({ queryClient: queryClient, persister: createSyncStoragePersister({ storage: window.localStorage, serialize: (data) => compress(JSON.stringify(data)), deserialize: (data) => JSON.parse(decompress(data)), }), maxAge: Infinity, }) ```

Installation of createSyncStoragePersister

To use createSyncStoragePersister, install the packages '@tanstack/query-sync-storage-persister' and '@tanstack/react-query-persist-client' using npm, pnpm, yarn, or bun.

Basic usage of createSyncStoragePersister

Import createSyncStoragePersister from '@tanstack/query-sync-storage-persister' and pass a storage object (such as window.localStorage or window.sessionStorage) to it. Pass the resulting persister to persistQueryClient along with a QueryClient instance.

createSyncStoragePersister is deprecated

The createSyncStoragePersister plugin is deprecated and will be removed in the next major version. Users should use '@tanstack/query-async-storage-persister' instead.

persistQueryClient combined function

persistQueryClient({queryClient, persister, maxAge = 1000 * 60 * 60 * 24, buster = '', hydrateOptions = undefined, dehydrateOptions = undefined}) performs two actions: it immediately restores any persisted cache (like persistQueryClientRestore), then subscribes to the query cache and returns the unsubscribe function (like persistQueryClientSubscribe). This functionality is preserved from version 3.x.

PersistQueryClientOptions interface

interface PersistQueryClientOptions { queryClient: QueryClient (required, the QueryClient to persist); persister: Persister (required, the Persister interface for storing and restoring cache); maxAge?: number (the max-allowed age of cache in milliseconds, defaults to 24 hours, older caches are silently discarded); buster?: string (unique string to forcefully invalidate caches without matching buster); hydrateOptions?: HydrateOptions (options passed to hydrate function, not used in persistQueryClientSave or persistQueryClientSubscribe); dehydrateOptions?: DehydrateOptions (options passed to dehydrate function, not used in persistQueryClientRestore) }

PersistQueryClientProvider for React

PersistQueryClientProvider is a React component that ensures proper subscription/unsubscription according to React component lifecycle and prevents queries from fetching during cache restoration. Queries will render but be in fetchingState: 'idle' until restoration is complete, then refetch unless the restored data is fresh enough. It respects initialData and can be used instead of QueryClientProvider.

persistQueryClient overview and purpose

persistQueryClient is a set of utilities for interacting with persisters that save the queryClient for later use. Different persisters can be used to store the client and cache to many different storage layers. Available persisters include createSyncStoragePersister, createAsyncStoragePersister, or custom persisters.

gcTime configuration for persistence

When using persistQueryClient, the QueryClient should be created with a gcTime value to override the default during hydration. If not set, gcTime defaults to 300000 (5 minutes) for hydration and the stored cache will be discarded after 5 minutes of inactivity. The gcTime should be set to the same value or higher than persistQueryClient's maxAge option. For example, if maxAge is 24 hours (the default), gcTime should be 24 hours or higher. If gcTime is lower than maxAge, garbage collection will discard the stored cache earlier than expected. You can pass Infinity to disable garbage collection entirely. The maximum allowed gcTime is about 24 days due to JavaScript limitations.

Cache busting with buster option

The buster option is a unique string used to forcefully invalidate existing caches if they do not share the same buster string. If a persisted cache is found that does not have the matching buster string, it will be discarded. The persistQueryClient, persistQueryClientSave, and persistQueryClientRestore functions all accept this option.

Cache removal conditions

The persister's removeClient() method is called and the cache is immediately discarded if any of the following conditions are met: the cache is expired (see maxAge), the cache is busted (see buster), an error occurs (ex: throws), or the cache is empty (ex: undefined).

persistQueryClientSave API

persistQueryClientSave({queryClient, persister, buster = '', dehydrateOptions = undefined}) dehydrates query/mutation data and stores it via the provided persister. The createSyncStoragePersister and createAsyncStoragePersister throttle this to happen at most every 1 second to reduce expensive writes. This function can be used to explicitly persist the cache at chosen moments.

persistQueryClientSubscribe API

persistQueryClientSubscribe({queryClient, persister, buster = '', dehydrateOptions = undefined}) runs persistQueryClientSave whenever the cache changes. It returns an unsubscribe function to discontinue monitoring and stop updates to the persisted cache. To erase the persisted cache after unsubscribing, pass a new buster to persistQueryClientRestore to trigger the persister's removeClient function.

persistQueryClientRestore API

persistQueryClientRestore({queryClient, persister, maxAge = 1000 * 60 * 60 * 24, buster = '', hydrateOptions = undefined}) attempts to hydrate a previously persisted dehydrated query/mutation cache from the persister back into the query cache. If a cache is found that is older than maxAge (24 hours by default), it will be discarded. This can be used to restore the cache at chosen moments.

PersistQueryClientProvider props

PersistQueryClientProvider takes all props of QueryClientProvider plus: persistOptions: PersistQueryClientOptions (all options from persistQueryClient minus the QueryClient itself); onSuccess?: () => Promise<unknown> | unknown (optional, called when initial restore finishes, can be used to resumePausedMutations, awaited if Promise is returned); onError?: () => Promise<unknown> | unknown (optional, called when error is thrown during restoration, awaited if Promise is returned).

useIsRestoring hook

useIsRestoring is a hook available when using PersistQueryClientProvider that checks if a restore is currently in progress. useQuery and other query hooks also check this internally to avoid race conditions between restore and mounting queries.

Persister interface

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

PersistedClient interface

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

Creating an IndexedDB persister example

import { get, set, del } from 'idb-keyval'; import { PersistedClient, Persister } from '@tanstack/react-query-persist-client'; 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 }. IndexedDB is faster than Web Storage API, stores more than 5MB, doesn't require serialization, and can store JavaScript native types like Date and File.

PersistQueryClientProvider usage example

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 } } }); const persister = createAsyncStoragePersister({ storage: window.localStorage }); ReactDOM.createRoot(rootElement).render(<PersistQueryClientProvider client={queryClient} persistOptions={{ persister }}><App /></PersistQueryClientProvider>);

Race condition issues with persistQueryClient

When using persistQueryClient directly without PersistQueryClientProvider, restoring is asynchronous and can cause race conditions if your App renders while restoring. If a query mounts and fetches at the same time as restoration is happening, race conditions can occur. Additionally, if you subscribe to changes outside of React component lifecycle, there is no way to unsubscribe.

HydrationBoundary API reference

HydrationBoundary component parameters: state (required, DehydratedState) - the state to hydrate; options (optional, HydrateOptions) - defaultOptions (optional, QueryOptions) - the default query options to use for the hydrated queries; queryClient (optional, QueryClient) - use custom QueryClient, otherwise one from nearest context will be used. Usage: <HydrationBoundary state={dehydratedState}>...</HydrationBoundary>

dehydrate function creates frozen cache representation

The dehydrate function creates a frozen representation of a cache that can later be hydrated with HydrationBoundary or hydrate. This is useful for passing prefetched queries from server to client or persisting queries to localStorage or other persistent locations. It only includes currently successful queries by default.

hydrate function adds dehydrated state to cache

The hydrate function adds a previously dehydrated state into a cache.

dehydrate API reference

dehydrate(queryClient, options) parameters: client (required, QueryClient) - the queryClient that should be dehydrated; options (optional, DehydrateOptions) - shouldDehydrateMutation (optional, function) - whether to dehydrate mutations, defaults to only including paused mutations, function called for each mutation returning true to include or false otherwise, can import defaultShouldDehydrateMutation to extend default behavior; shouldDehydrateQuery (optional, function) - whether to dehydrate queries, defaults to only including successful queries, function called for each query returning true to include or false otherwise, can import defaultShouldDehydrateQuery to extend default behavior; serializeData (optional, function) - transforms data during dehydration; shouldRedactErrors (optional, function) - whether to redact errors from server, defaults to redacting all errors, function called for each error returning true to redact or false otherwise. Returns DehydratedState containing everything needed to hydrate the queryClient later. The exact format is not part of the public API and can change. Result is not in serialized form, serialization must be done manually if desired.

dehydrate limitation with non-JSON serializable values

Some storage systems like browser Web Storage API require values to be JSON serializable. If dehydrating values that are not automatically JSON serializable like Error or undefined, they must be manually serialized. Since only successful queries are included by default, to also include Errors, provide shouldDehydrateQuery option and serialize Error instances to objects before storing, then deserialize objects back to Error instances when hydrating.

dehydrate with Error objects example

Example showing how to handle non-JSON serializable values when dehydrating: on server, call dehydrate(client, { shouldDehydrateQuery: () => true }) to include Errors, then transform with mySerialize(state) to transform Error instances to objects; on client, call myDeserialize(serializedState) to transform objects back to Error instances, then hydrate(client, state).

hydrate API reference

hydrate(queryClient, dehydratedState, options) parameters: client (required, QueryClient) - the queryClient to hydrate the state into; dehydratedState (required, DehydratedState) - the state to hydrate into the client; options (optional, HydrateOptions) - defaultOptions (optional, DefaultOptions) with mutations (optional, MutationOptions) for default mutation options and queries (optional, QueryOptions) for default query options, deserializeData (optional, function) - transforms data before it is put into the cache; queryClient (optional, QueryClient) - use custom QueryClient, otherwise one from nearest context will be used.

hydrate limitation with existing queries in cache

If queries being hydrated already exist in the queryCache, hydrate will only overwrite them if the data is newer than the data present in the cache. Otherwise, the new data will not be applied.

HydrationBoundary component adds dehydrated state to queryClient

HydrationBoundary adds a previously dehydrated state into the queryClient that would be returned by useQueryClient(). If the client already contains data, the new queries will be intelligently merged based on update timestamp. Only queries can be dehydrated with HydrationBoundary, not mutations.

Give your agent this brain