这是一组用于与“持久化程序”交互的实用程序,这些持久化程序会保存您的 queryClient 以供将来使用。可以使用不同的持久化程序将您的客户端和缓存存储到许多不同的存储层。
重要提示 - 为了使持久化正常工作,您可能希望向 QueryClient 传递一个 gcTime 值以在水合期间覆盖默认值(如上所示)。
如果在创建 QueryClient 实例时未设置该值,则对于水合,它将默认为 300000(5 分钟),并且存储的缓存将在 5 分钟不活动后被丢弃。这是默认的垃圾回收行为。
它应设置为与 persistQueryClient 的 maxAge 选项相同或更高的值。例如,如果 maxAge 为 24 小时(默认值),则 gcTime ��为 24 小时或更高。如果低于 maxAge,垃圾回收将介入并比预期更早地丢弃存储的缓存。
您也可以将其传递为 Infinity 以完全禁用垃圾回收行为。
由于 Javascript 的限制,允许的最大 gcTime 约为 24 天(请参阅更多)。
const queryClient = new QueryClient({
defaultOptions: {
queries: {
gcTime: 1000 * 60 * 60 * 24, // 24 小时
},
},
})
const queryClient = new QueryClient({
defaultOptions: {
queries: {
gcTime: 1000 * 60 * 60 * 24, // 24 小时
},
},
})
有时,您可能对应用程序或数据进行了更改,从而立即使所有缓存数据失效。如果发生这种情况,您可以传递一个 buster 字符串选项。如果找到的缓存也没有该 buster 字符串,则将其丢弃。以下几个函数接受此选项:
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 })
如果发现数据属于以下任何一种情况:
则会调用持久化程序的 removeClient(),并立即丢弃缓存。
您可以使用此功能在您选择的时刻显式持久化缓存。
persistQueryClientSave({
queryClient,
persister,
buster = '',
dehydrateOptions = undefined,
})
persistQueryClientSave({
queryClient,
persister,
buster = '',
dehydrateOptions = undefined,
})
每当 queryClient 的缓存发生更改时,都会运行 persistQueryClientSave。例如:您可能会在用户登录并选中“记住我”时启动 subscribe。
persistQueryClientSubscribe({
queryClient,
persister,
buster = '',
dehydrateOptions = undefined,
})
persistQueryClientSubscribe({
queryClient,
persister,
buster = '',
dehydrateOptions = undefined,
})
您可以使用此功能在您选择的时刻恢复缓存。
persistQueryClientRestore({
queryClient,
persister,
maxAge = 1000 * 60 * 60 * 24, // 24 小时
buster = '',
hydrateOptions = undefined,
})
persistQueryClientRestore({
queryClient,
persister,
maxAge = 1000 * 60 * 60 * 24, // 24 小时
buster = '',
hydrateOptions = undefined,
})
执行以下操作:
此功能从版本 3.x 保留下来。
persistQueryClient({
queryClient,
persister,
maxAge = 1000 * 60 * 60 * 24, // 24 小时
buster = '',
hydrateOptions = undefined,
dehydrateOptions = undefined,
})
persistQueryClient({
queryClient,
persister,
maxAge = 1000 * 60 * 60 * 24, // 24 小时
buster = '',
hydrateOptions = undefined,
dehydrateOptions = undefined,
})
所有可用选项如下:
interface PersistQueryClientOptions {
/** 要持久化的 QueryClient */
queryClient: QueryClient
/** 用于将缓存存储和恢复到/从持久化位置的 Persister 接口 */
persister: Persister
/** 缓存的最大允许存在时间(以毫秒为单位)。
* 如果找到的持久化缓存早于此
* 时间,则将其**静默**丢弃
* (默认为 24 小时) */
maxAge?: number
/** 一个唯一字符串,可用于强制
* 使现有缓存失效(如果它们不共享相同的 buster 字符串) */
buster?: string
/** 传递给水合函数的选项
* 未在 `persistQueryClientSave` 或 `persistQueryClientSubscribe` 上使用 */
hydrateOptions?: HydrateOptions
/** 传递给脱水函数的选项
* 未在 `persistQueryClientRestore` 上使用 */
dehydrateOptions?: DehydrateOptions
}
interface PersistQueryClientOptions {
/** 要持久化的 QueryClient */
queryClient: QueryClient
/** 用于将缓存存储和恢复到/从持久化位置的 Persister 接口 */
persister: Persister
/** 缓存的最大允许存在时间(以毫秒为单位)。
* 如果找到的持久化缓存早于此
* 时间,则将其**静默**丢弃
* (默认为 24 小时) */
maxAge?: number
/** 一个唯一字符串,可用于强制
* 使现有缓存失效(如果它们不共享相同的 buster 字符串) */
buster?: string
/** 传递给水合函数的选项
* 未在 `persistQueryClientSave` 或 `persistQueryClientSubscribe` 上使用 */
hydrateOptions?: HydrateOptions
/** 传递给脱水函数的选项
* 未在 `persistQueryClientRestore` 上使用 */
dehydrateOptions?: DehydrateOptions
}
实际上有三个可用的接口:
persistQueryClient 将尝试恢复缓存并自动订阅后续更改,从而将您的客户端同步到提供的存储。
但是,恢复是异步的,因为所有持久化程序本质上都是异步的,这意味着如果您在恢复时渲染您的应用程序,如果查询同时挂载和获取,则可能会遇到竞争条件。
此外,如果您在 React 组件生命周期之外订阅更改,则无法取消订阅:
// 🚨 从不同步中永不取消订阅
persistQueryClient({
queryClient,
persister: localStoragePersister,
})
// 🚨 与恢复同时发生
ReactDOM.createRoot(rootElement).render(<App />)
// 🚨 从不同步中永不取消订阅
persistQueryClient({
queryClient,
persister: localStoragePersister,
})
// 🚨 与恢复同时发生
ReactDOM.createRoot(rootElement).render(<App />)
对于此用例,您可以使用 PersistQueryClientProvider。它将确保根据 React 组件生命周期正确订阅/取消订阅,并且还将确保在仍在恢复时查询不会开始获取。但是,查询仍将渲染,它们只会被置于 fetchingState: 'idle' 直到数据恢复。然后,除非恢复的数据足够_新鲜_,否则它们将重新获取,并且_initialData_ 也将得到遵守。它可以_代替_普通的 QueryClientProvider 使用:
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 小时
},
},
})
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 小时
},
},
})
const persister = createAsyncStoragePersister({
storage: window.localStorage,
})
ReactDOM.createRoot(rootElement).render(
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister }}
>
<App />
</PersistQueryClientProvider>,
)
PersistQueryClientProvider 接受与 QueryClientProvider 相同的属性,此外还有:
如果您正在使用 PersistQueryClientProvider,则还可以使用 useIsRestoring 钩子来检查当前是否正在进行恢复。useQuery 及其相关函数也会在内部检查此钩子,以避免恢复和挂载查询之间的竞争条件。
持久化程序具有以下接口:
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>
}
持久化客户端条目具有以下接口:
export interface PersistedClient {
timestamp: number
buster: string
clientState: DehydratedState
}
export interface PersistedClient {
timestamp: number
buster: string
clientState: DehydratedState
}
您可以导入这些(以构建持久化程序):
import {
PersistedClient,
Persister,
} from '@tanstack/react-query-persist-client'
import {
PersistedClient,
Persister,
} from '@tanstack/react-query-persist-client'
您可以随心所欲地进行持久化。以下是如何构建 Indexed DB 持久化程序的示例。与 Web Storage API 相比,Indexed DB 更快,存储量超过 5MB,并且不需要序列化。这意味着它可以轻松存储 Javascript 原生类型,例如 Date 和 File。
import { get, set, del } from 'idb-keyval'
import {
PersistedClient,
Persister,
} from '@tanstack/react-query-persist-client'
/**
* 创建一个 Indexed DB 持久化程序
* @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'
/**
* 创建一个 Indexed DB 持久化程序
* @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
}