-
Hi, we are migrating from Redux to TQ in a pretty big enterprise application. However, my question is this: what is the best way to set this up if we only want to persist some queries in indexeddb and not every query? Would this be the correct way to go about it? import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { persistQueryClient, PersistQueryClientOptions } from '@tanstack/react-query-persist-client';
import { openDB } from 'idb';
import { useState, useEffect } from 'react';
// Function to create an IndexedDB persister
const createIDBPersister = (dbName = 'tanstack-query-cache', storeName = 'queries') => {
return {
persistClient: async (clientState) => {
const db = await openDB(dbName, 1, {
upgrade(db) {
if (!db.objectStoreNames.contains(storeName)) {
db.createObjectStore(storeName);
}
},
});
await db.put(storeName, clientState, 'tanstack-query');
},
restoreClient: async () => {
const db = await openDB(dbName, 1);
return (await db.get(storeName, 'tanstack-query')) || null;
},
removeClient: async () => {
const db = await openDB(dbName, 1);
await db.delete(storeName, 'tanstack-query');
},
};
};
// Create a QueryClient with default staleTime set to Infinity
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity, // Default behavior remains unchanged
},
},
});
// App Component - Ensures the QueryClient is rehydrated before being used
const App = ({ children }) => {
const [isRehydrated, setIsRehydrated] = useState(false);
const persister = createIDBPersister();
useEffect(() => {
persistQueryClient({
queryClient,
persister,
dehydrateOptions: {
shouldDehydrateQuery: (query) =>
query.queryKey[0] === 'translations' || query.queryKey[0] === 'routes',
},
} as PersistQueryClientOptions).then(() => {
setIsRehydrated(true); // Ensure the app waits for rehydration
});
}, []);
if (!isRehydrated) return <div>Loading...</div>; // Prevents using stale state before rehydration
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
};
export default App; |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 1 reply
-
please use the |
Beta Was this translation helpful? Give feedback.
-
Does this not by default store all queries? |
Beta Was this translation helpful? Give feedback.
-
Thanks @TkDodo for the quick replies. May be worth adding an example of this to the docs. We ended up using import React, { Suspense, useEffect, useState } from 'react';
import { QueryClient } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
import { createIDBPersister } from './utils';
declare global {
interface Window {
toggleDevtools: () => void;
}
}
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
gcTime: Infinity,
refetchOnReconnect: true,
refetchOnMount: false,
refetchOnWindowFocus: false,
},
},
});
const persister = createIDBPersister();
interface Props {
children: React.ReactNode;
}
const ReactQueryDevtoolsProduction = React.lazy(() =>
// eslint-disable-next-line import/extensions
import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({
default: d.ReactQueryDevtools,
}))
);
const ReactQueryProvider: React.FC<Props> = ({ children }) => {
const [shouldShowProdDevtools, setShouldShowProdDevtools] = useState(false);
useEffect(() => {
window.toggleDevtools = () => setShouldShowProdDevtools((prev) => !prev);
}, []);
return (
<PersistQueryClientProvider
client={queryClient}
persistOptions={{
persister,
dehydrateOptions: {
shouldDehydrateQuery: (query) => {
return query.meta?.persist === true;
},
},
}}
>
{children}
<ReactQueryDevtools buttonPosition="bottom-left" />
{shouldShowProdDevtools && (
<Suspense fallback={null}>
<ReactQueryDevtoolsProduction buttonPosition="bottom-left" />
</Suspense>
)}
</PersistQueryClientProvider>
);
};
export default ReactQueryProvider; and expanding the useQuery type: import { UseQueryOptions } from '@tanstack/react-query';
declare module '@tanstack/react-query' {
interface UseQueryOptions<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData> {
meta?: {
persist?: boolean; // Add the persist property
};
}
} Persister: import type { PersistedClient } from '@tanstack/react-query-persist-client';
import type { IDBPDatabase } from 'idb';
import { openDB } from 'idb';
export const createIDBPersister = (dbName = 'tanstack-query-cache', storeName = 'queries') => {
const dbConfig = {
upgrade(database: IDBPDatabase) {
if (!database.objectStoreNames.contains(storeName)) {
database.createObjectStore(storeName, { keyPath: 'id' });
}
},
};
return {
persistClient: async (clientState: PersistedClient) => {
const database = await openDB(dbName, 1, dbConfig);
try {
const tx = database.transaction(storeName, 'readwrite');
const store = tx.objectStore(storeName);
await store.put(clientState);
await tx.done;
} finally {
database.close();
}
},
restoreClient: async () => {
const database = await openDB(dbName, 1, dbConfig);
try {
const tx = database.transaction(storeName, 'readonly');
const store = tx.objectStore(storeName);
return await store.get('tanstack');
} finally {
database.close();
}
},
removeClient: async () => {
const database = await openDB(dbName, 1, dbConfig);
try {
const tx = database.transaction(storeName, 'readwrite');
const store = tx.objectStore(storeName);
await store.delete('tanstack');
await tx.done;
} finally {
database.close();
}
},
};
}; |
Beta Was this translation helpful? Give feedback.
Thanks @TkDodo for the quick replies. May be worth adding an example of this to the docs.
We ended up using
dehydrateOptions
, will provide all code here for future reference: