框架
版本

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 接口的 存储 传递给它 - 以下示例使用了 React Native 的 async-storage。
  • 将此 persister 作为选项传递给您的 Query。这可以通过将其传递给 QueryClientdefaultOptions 或任何 useQuery hook 实例来完成。
    • 如果您将此 persister 作为 defaultOptions 传递,所有查询都将被持久化到提供的 storage。您还可以通过传递 filters 来进一步缩小范围。与 persistClient 插件不同,这不会将整个 query client 作为一个单独的项目进行持久化,而是将每个查询单独持久化。作为键,使用查询的 hash。
    • 如果您将此 persister 传递给单个 useQuery hook,则只有该 Query 会被持久化。
  • 注意: queryClient.setQueryData() 操作不会被持久化,这意味着如果您在查询失效之前进行了乐观更新并刷新了页面,您的查询数据更改将丢失。请参阅 https://github.com/TanStack/query/issues/6310

这样,您就不需要存储整个 QueryClient,而是可以选择在应用程序中值得持久化的内容。每个查询都会被惰性恢复(在首次使用该 Query 时)并持久化(在每次运行 queryFn 之后),因此无需进行节流。 staleTime 在恢复 Query 后也会被尊重,因此如果数据被视为 stale,它将在恢复后立即重新获取。如果数据是 fresh,则 queryFn 不会运行。

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

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 hours
})

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 30, // 30 seconds
      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 hours
})

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

调整默认值

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

附加工具

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

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

此函数将 Query 持久化到存储中,并使用创建 persister 时定义的键。
此工具可能与 setQueryData 一起使用,以将乐观更新持久化到存储中,而无需等待失效。

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

const queryClient = useQueryClient()

useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    ...
    // Optimistically update to the new value
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
    // And persist it to storage
    persister.persistQueryByKey(['todos'], queryClient)
    ...
  },
})
const persister = experimental_createQueryPersister({
  storage: AsyncStorage,
  maxAge: 1000 * 60 * 60 * 12, // 12 hours
})

const queryClient = useQueryClient()

useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    ...
    // Optimistically update to the new value
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
    // And persist it to storage
    persister.persistQueryByKey(['todos'], queryClient)
    ...
  },
})

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

此函数将尝试通过 queryHash 检索已持久化的查询。
如果 query过期损坏格式错误,它将被从存储中删除,并返回 undefined

persisterGc(): Promise<void>

此函数可用于零星地清理存储中 过期损坏格式错误 的条目。

为了使此函数正常工作,您的存储必须公开 entries 方法,该方法将返回一个 键值对数组
例如, localStorageObject.entries(localStorage)idb-keyvalentries

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

此函数可用于恢复当前由 persister 存储的查询。
例如,当您的应用程序在离线模式下启动时,或者您希望上一个会话的所有或部分数据在没有中间 loading 状态的情况下立即可用。

filter 对象支持以下属性

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

为了使此函数正常工作,您的存储必须公开 entries 方法,该方法将返回一个 键值对数组
例如, localStorageObject.entries(localStorage)idb-keyvalentries

API

experimental_createQueryPersister

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

选项

tsx
export interface StoragePersisterOptions {
  /** The storage client used for setting and retrieving items from cache.
   * For SSR pass in `undefined`.
   */
  storage: AsyncStorage | Storage | undefined | null
  /**
   * How to serialize the data to storage.
   * @default `JSON.stringify`
   */
  serialize?: (persistedQuery: PersistedQuery) => string
  /**
   * How to deserialize the data from storage.
   * @default `JSON.parse`
   */
  deserialize?: (cachedString: string) => PersistedQuery
  /**
   * A unique string that can be used to forcefully invalidate existing caches,
   * if they do not share the same buster string
   */
  buster?: string
  /**
   * The max-allowed age of the cache in milliseconds.
   * If a persisted cache is found that is older than this
   * time, it will be discarded
   * @default 24 hours
   */
  maxAge?: number
  /**
   * Prefix to be used for storage key.
   * Storage key is a combination of prefix and query hash in a form of `prefix-queryHash`.
   */
  prefix?: string
  /**
   * Filters to narrow down which Queries should be persisted.
   */
  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 {
  /** The storage client used for setting and retrieving items from cache.
   * For SSR pass in `undefined`.
   */
  storage: AsyncStorage | Storage | undefined | null
  /**
   * How to serialize the data to storage.
   * @default `JSON.stringify`
   */
  serialize?: (persistedQuery: PersistedQuery) => string
  /**
   * How to deserialize the data from storage.
   * @default `JSON.parse`
   */
  deserialize?: (cachedString: string) => PersistedQuery
  /**
   * A unique string that can be used to forcefully invalidate existing caches,
   * if they do not share the same buster string
   */
  buster?: string
  /**
   * The max-allowed age of the cache in milliseconds.
   * If a persisted cache is found that is older than this
   * time, it will be discarded
   * @default 24 hours
   */
  maxAge?: number
  /**
   * Prefix to be used for storage key.
   * Storage key is a combination of prefix and query hash in a form of `prefix-queryHash`.
   */
  prefix?: string
  /**
   * Filters to narrow down which Queries should be persisted.
   */
  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,
}