框架
版本

experimental_createQueryPersister

安装

此实用程序作为一个单独的包提供,可通过 '@tanstack/query-persist-client-core' 导入。

bash
npm install @tanstack/query-persist-client-core
npm install @tanstack/query-persist-client-core

或者

bash
pnpm add @tanstack/query-persist-client-core
pnpm add @tanstack/query-persist-client-core

或者

bash
yarn add @tanstack/query-persist-client-core
yarn add @tanstack/query-persist-client-core

或者

bash
bun add @tanstack/query-persist-client-core
bun add @tanstack/query-persist-client-core

注意:此实用程序也包含在 @tanstack/solid-query-persist-client 包中,因此如果您正在使用该包,则无需单独安装它。

用法

  • 导入 experimental_createQueryPersister 函数
  • 创建一个新的 experimental_createQueryPersister
    • 您可以向其传递任何符合 AsyncStorage 接口的 storage - 下面的示例使用来自 React Native 的 async-storage。
  • 将该 persister 作为选项传递给您的查询。这可以通过将其传递给 QueryClientdefaultOptions 或任何 useQuery 钩子实例来完成。
    • 如果您将此 persister 作为 defaultOptions 传递,则所有查询都将持久化到提供的 storage 中。您还可以通过传递 filters 来进一步缩小范围。与 persistClient 插件相反,这不会将整个查询客户端持久化为单个项目,而是分别持久化每个查询。查询哈希用作键。
    • 如果您将此 persister 提供给单个 useQuery 钩子,则仅持久化此查询。
  • 注意:queryClient.setQueryData() 操作不会被持久化,这意味着如果您执行乐观更新并在查询失效之前刷新页面,您对查询数据的更改将会丢失。请参阅 https://github.com/TanStack/query/issues/6310

这样,您无需存储整个 QueryClient,而是可以选择在应用程序中值得持久化的内容。每个查询都是延迟恢复(首次使用查询时)和持久化(每次运行 queryFn 后),因此不需要对其进行节流。恢复查询后也会遵守 staleTime,因此如果数据被认为是 stale,则在恢复后会立即重新获取。如果数据是 fresh,则不会运行 queryFn

从内存中进行垃圾回收查询不会影响持久化数据。这意味着查询可以在内存中保留较短的时间以提高内存效率。如果下次使用它们,它们将再次从持久存储中恢复。

tsx
import AsyncStorage from '@react-native-async-storage/async-storage'
import { QueryClient } from '@tanstack/solid-query'
import { experimental_createQueryPersister } from '@tanstack/query-persist-client-core'

const persister = experimental_createQueryPersister({
  storage: AsyncStorage,
  maxAge: 1000 * 60 * 60 * 12, // 12 小时
})

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 30, // 30 秒
      persister: persister.persisterFn,
    },
  },
})
import AsyncStorage from '@react-native-async-storage/async-storage'
import { QueryClient } from '@tanstack/solid-query'
import { experimental_createQueryPersister } from '@tanstack/query-persist-client-core'

const persister = experimental_createQueryPersister({
  storage: AsyncStorage,
  maxAge: 1000 * 60 * 60 * 12, // 12 小时
})

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 30, // 30 秒
      persister: persister.persisterFn,
    },
  },
})

调整后的默认值

createPersister 插件在技术上包装了 queryFn,因此如果 queryFn 不运行,它就不会恢复。这样,它充当查询和网络之间的缓存层。因此,当使用持久化程序时,networkMode 默认为 'offlineFirst',以便即使在没有网络连接的情况下也可以从持久存储中恢复。

其他实用程序

调用 experimental_createQueryPersister 除了返回 persisterFn 外,还会返回其他实用程序,以便更轻松地实现用户区功能。

persistQueryByKey(queryKey: QueryKey, queryClient: QueryClient): Promise<void>

此函数会将 Query 持久���到创建持久化程序时定义的存储和键中。 此实用程序可与 setQueryData 一起使用,以将乐观更新持久化到存储中,而无需等待失效。

tsx
const persister = experimental_createQueryPersister({
  storage: AsyncStorage,
  maxAge: 1000 * 60 * 60 * 12, // 12 小时
})

const queryClient = useQueryClient()

useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    ...
    // 乐观地更新到新值
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
    // 并将其持久化到存储中
    persister.persistQueryByKey(['todos'], queryClient)
    ...
  },
})
const persister = experimental_createQueryPersister({
  storage: AsyncStorage,
  maxAge: 1000 * 60 * 60 * 12, // 12 小时
})

const queryClient = useQueryClient()

useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    ...
    // 乐观地更新到新值
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
    // 并将其持久化到存储中
    persister.persistQueryByKey(['todos'], queryClient)
    ...
  },
})

retrieveQuery<T>(queryHash: string): Promise<T | undefined>

此函数将尝试通过 queryHash 检索持久化查询。 如果 queryexpiredbustedmalformed,则会将其从存储中删除,并返回 undefined

persisterGc(): Promise<void>

此函数可用于定期清理存储中 expiredbustedmalformed 的条目。

要使此函数正常工作,您的存储必须公开 entries 方法,该方法将返回一个 键值元组数组。 例如,对于 localStorage,为 Object.entries(localStorage),或对于 idb-keyval,为 entries

restoreQueries(queryClient: QueryClient, filters): Promise<void>

此函数可用于恢复当前由持久化程序存储的查询。 例如,当您的应用程序在离线模式下启动时,或者您希望立即获得上一个会话的全部或仅特定数据而无需中间的 loading 状态时。

过滤器对象支持以下属性:

  • queryKey?: QueryKey
    • 设置此属性以定义要匹配的查询键。
  • exact?: boolean
    • 如果您不想按查询键进行包含性搜索查询,可以传递 exact: true 选项以仅返回具有您传递的确切查询键的查询。

要使此函数正常工作,您的存储必须公开 entries 方法,该方法将返回一个 键值元组数组。 例如,对于 localStorage,为 Object.entries(localStorage),或对于 idb-keyval,为 entries

API

experimental_createQueryPersister

tsx
experimental_createQueryPersister(options: StoragePersisterOptions)
experimental_createQueryPersister(options: StoragePersisterOptions)

选项

tsx
export interface StoragePersisterOptions {
  /** 用于从缓存中设置和检索项目的存储客户端。
   * 对于 SSR,请传入 `undefined`。
   */
  storage: AsyncStorage | Storage | undefined | null
  /**
   * 如何将数据序列化到存储。
   * @default `JSON.stringify`
   */
  serialize?: (persistedQuery: PersistedQuery) => string
  /**
   * 如何从存储中反序列化数据。
   * @default `JSON.parse`
   */
  deserialize?: (cachedString: string) => PersistedQuery
  /**
   * 一个唯一字符串,可用于强制使现有缓存失效,
   * 如果它们不共享相同的 buster 字符串
   */
  buster?: string
  /**
   * 缓存的最大允许存在时间(以毫秒为单位)。
   * 如果发现持久化缓存早于此
   * 时间,则将其丢弃
   * @default 24 小时
   */
  maxAge?: number
  /**
   * 用于存储键的前缀。
   * 存储键是前缀和查询哈希的组合,形式为 `prefix-queryHash`。
   */
  prefix?: string
  /**
   * 用于缩小应持久化的查询范围的过滤器。
   */
  filters?: QueryFilters
}

interface AsyncStorage<TStorageValue = string> {
  getItem: (key: string) => MaybePromise<TStorageValue | undefined | null>
  setItem: (key: string, value: TStorageValue) => MaybePromise<unknown>
  removeItem: (key: string) => MaybePromise<void>
  entries?: () => MaybePromise<Array<[key: string, value: TStorageValue]>>
}
export interface StoragePersisterOptions {
  /** 用于从缓存中设置和检索项目的存储客户端。
   * 对于 SSR,请传入 `undefined`。
   */
  storage: AsyncStorage | Storage | undefined | null
  /**
   * 如何将数据序列化到存储。
   * @default `JSON.stringify`
   */
  serialize?: (persistedQuery: PersistedQuery) => string
  /**
   * 如何从存储中反序列化数据。
   * @default `JSON.parse`
   */
  deserialize?: (cachedString: string) => PersistedQuery
  /**
   * 一个唯一字符串,可用于强制使现有缓存失效,
   * 如果它们不共享相同的 buster 字符串
   */
  buster?: string
  /**
   * 缓存的最大允许存在时间(以毫秒为单位)。
   * 如果发现持久化缓存早于此
   * 时间,则将其丢弃
   * @default 24 小时
   */
  maxAge?: number
  /**
   * 用于存储键的前缀。
   * 存储键是前缀和查询哈希的组合,形式为 `prefix-queryHash`。
   */
  prefix?: string
  /**
   * 用于缩小应持久化的查询范围的过滤器。
   */
  filters?: QueryFilters
}

interface AsyncStorage<TStorageValue = string> {
  getItem: (key: string) => MaybePromise<TStorageValue | undefined | null>
  setItem: (key: string, value: TStorageValue) => MaybePromise<unknown>
  removeItem: (key: string) => MaybePromise<void>
  entries?: () => MaybePromise<Array<[key: string, value: TStorageValue]>>
}

默认选项是:

tsx
{
  prefix = 'tanstack-query',
  maxAge = 1000 * 60 * 60 * 24,
  serialize = JSON.stringify,
  deserialize = JSON.parse,
}
{
  prefix = 'tanstack-query',
  maxAge = 1000 * 60 * 60 * 24,
  serialize = JSON.stringify,
  deserialize = JSON.parse,
}