본문으로 건너뛰기

experimental_createQueryPersister

설치

이 유틸리티는 별도 패키지로 제공되며 '@tanstack/query-persist-client-core' import를 통해 사용할 수 있습니다.

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

or

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

or

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

or

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

사용법

  • experimental_createQueryPersister 함수 가져오기
  • experimental_createQueryPersister 생성
    • AsyncStorage 인터페이스를 준수하는 모든 storage를 여기에 전달할 수 있습니다.
  • 해당 persister를 Query의 옵션으로 전달합니다. QueryClientdefaultOptions에 전달하거나 useQuery 훅 인스턴스에 전달하면 됩니다.
    • persisterdefaultOptions로 전달하면 모든 쿼리가 제공된 storage에 영구 저장됩니다. filters를 전달하여 범위를 추가로 좁힐 수 있습니다. persistClient 플러그인과 달리 전체 query client를 단일 항목으로 영구 저장하지 않고 각 쿼리를 개별적으로 영구 저장합니다. 키로는 query hash가 사용됩니다.
    • 단일 useQuery 훅에 이 persister를 제공하면 이 Query만 영구 저장됩니다.
  • 참고: queryClient.setQueryData() 작업은 영구 저장되지 않습니다. 즉, 낙관적 업데이트를 수행한 후 쿼리가 무효화되기 전에 페이지를 새로 고치면 쿼리 데이터에 적용한 변경 사항이 손실됩니다. https://github.com/TanStack/query/issues/6310을 참조하세요.

이 방식에서는 QueryClient 전체를 저장할 필요 없이 애플리케이션에서 영구 저장할 가치가 있는 항목을 선택할 수 있습니다. 각 쿼리는 지연 복원되고(Query가 처음 사용될 때) 영구 저장되므로(queryFn을 실행할 때마다) 스로틀링할 필요가 없습니다. Query를 복원한 후에도 staleTime이 적용되므로 데이터가 stale 상태로 간주되면 복원 직후 다시 가져옵니다. 데이터가 fresh 상태라면 queryFn은 실행되지 않습니다.

메모리에서 Query를 가비지 컬렉션하는 것은 영구 저장된 데이터에 영향을 주지 않습니다. 즉, 메모리를 더 효율적으로 사용하기 위해 Queries를 메모리에 더 짧은 기간 동안 유지할 수 있습니다. 다음에 사용하면 영구 저장소에서 다시 복원됩니다.

import { QueryClient } from '@tanstack/vue-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와 함께 사용할 수 있습니다.

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로 영구 저장된 쿼리를 검색하려고 시도합니다.
queryexpired, busted 또는 malformed이면 대신 저장소에서 제거되고 undefined가 반환됩니다.

persisterGc(): Promise<void>

이 함수는 expired, busted 또는 malformed 항목을 저장소에서 간헐적으로 정리하는 데 사용할 수 있습니다.

이 함수가 작동하려면 저장소에서 key-value tuple array를 반환하는 entries 메서드를 노출해야 합니다.
예를 들어 localStorage에 대한 Object.entries(localStorage) 또는 idb-keyvalentries입니다.

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

이 함수는 현재 persister에 저장된 쿼리를 복원하는 데 사용할 수 있습니다.
예를 들어 앱이 오프라인 모드에서 시작되거나, 중간 loading 상태 없이 이전 세션의 모든 데이터 또는 특정 데이터만 즉시 사용할 수 있게 하려는 경우입니다.

필터 객체는 다음 속성을 지원합니다:

  • queryKey?: QueryKey
    • 일치시킬 쿼리 키를 정의하려면 이 속성을 설정합니다.
  • exact?: boolean
    • 쿼리 키를 기준으로 쿼리를 포괄적으로 검색하지 않으려면 exact: true 옵션을 전달하여 지정한 쿼리 키와 정확히 일치하는 쿼리만 반환할 수 있습니다.

이 함수가 작동하려면 저장소에서 key-value tuple array를 반환하는 entries 메서드를 노출해야 합니다.
예를 들어 localStorage에 대한 Object.entries(localStorage) 또는 idb-keyvalentries입니다.

API

experimental_createQueryPersister

experimental_createQueryPersister(options: StoragePersisterOptions)

Options

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
/**
* If set to `true`, the query will refetch on successful query restoration if the data is stale.
* If set to `false`, the query will not refetch on successful query restoration.
* If set to `'always'`, the query will always refetch on successful query restoration.
* Defaults to `true`.
*/
refetchOnRestore?: boolean | 'always'
/**
* 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]>>
}

기본 옵션은 다음과 같습니다:

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