react-query(v4) - persistQueryClient 플러그인

hwisaac·2023년 2월 8일
0

리액트쿼리

목록 보기
5/5

persistQueryClient 플러그인

[persistQueryClient]

  • persistQueryClient는 나중에 사용할 수 있도록 쿼리 클라이언트를 저장하는 perister와 상호 작용하기 위한 유틸리티 집합입니다. 다양한 persister들를 사용하여 클라이언트 및 캐시를 다양한 스토리지 레이어에 저장할 수 있습니다.

Build Persisters

작동방식

cacheTime 에 대하여

  • persist가 제대로 작동하려면 hydration 도중에 디폴트값을 재정의하기 위해 QueryClientcacheTime 값을 전달해야 될 수 있습니다.

  • QueryClient 인스턴스를 만들 때 cacheTime 값을 설정하지 않으면 hydration를 위해 디폴트 값이 300000(5분)으로 설정되며 저장된 캐시는 5분 동안 비활성화된 후 삭제됩니다.

  • persistQueryClient 의 maxAge 옵션의 값보다 같거나 큰 값으로 설정해야 합니다.

    • 예를 들어 maxAge가 24시간(기본값)이면 cacheTime은 24시간 이상이어야 합니다. maxAge보다 작으면 가비지 컬렉션이 시작되고 저장된 캐시가 예상보다 빨리 삭제됩니다.
  • 가바지 컬렉션을 작동하지 않게 하려면 Infinity 값으로 설정하면 됩니다.

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      cacheTime: 1000 * 60 * 60 * 24, // 24 hours
    },
  },
})

Cache Busting

  • 어플리케이션이나 데이터를 변경하여 캐시된 모든 데이터를 즉시 무효화할 수도 있습니다.
  • 이 현상이 일어나면, buster 옵션을 전달할 수 있습니다.
  • 캐시를 얻어냈는데 해당 buster 문자열이 없으면 캐시가 삭제됩니다. 이 옵션을 사용할 수 있는 기능은 다음과 같습니다:
persistQueryClient({ queryClient, persister, buster: buildHash })
persistQueryClientSave({ queryClient, persister, buster: buildHash })
persistQueryClientRestore({ queryClient, persister, buster: buildHash })

Removal

  • 얻어낸 데이터가 다음중 하나에 해당되면 removeClient()가 호출되고 캐시가 즉시 삭제됩니다.
    1. expired
    2. busted
    3. error
    4. empy

API

persistQueryClientSave

  • query/mutation 는 persister 에 의해 dehydrated 상태가 되고 저장됩니다.
  • createSyncStoragePersistercreateAsyncStoragePersister 는 throttle 합니다. 이 작업은 잠재적으로 비용이 많이 드는 쓰기 작업을 절약하기 위해 최대 1초마다 수행됩니다. 스로틀 타이밍을 사용자 지정하는 방법을 보려면 해당 문서를 검토하십시오.

원하는 타이밍에 명시적으로 캐시에 persist 하기 위해 이렇게 사용할 수 있습니다.

persistQueryClientSave({
  queryClient,
  persister,
  buster = '',
  dehydrateOptions = undefined,
})

persistQueryClientSubscribe

queryClient의 캐시가 변경될 때마다 persistQueryClientSave 가 실행됩니다. 예를 들어, 사용자가 로그인하고 "Remember me"를 확인할 때 subscribe를 시작할 수 있습니다.

  • 이것은 unsubscribe 함수를 리턴합니다 : 모니터링을 중단하고 지속적인 캐시에 대한 업데이트를 종료하는 데 사용할 수 있습니다.
  • 당신이 unsubscribe 이후 persisted cache를 삭제하고 싶을 경우, 새로운 busterpersistQueryClientRestore에 보낼 수 있는데 이 것이 persister 의 removeClient 함수의 트리거가 됩니다. 그리고 persisted cache 가 삭제됩니다.
persistQueryClientSubscribe({
  queryClient,
  persister,
  buster = '',
  dehydrateOptions = undefined,
})

persistQueryClientRestore

  • persited dehydrated query/mutation cache 에다가 hydrate를 시도하여 쿼리 client 에 넘겨졌던 query cache 로 되돌립니다.
  • 만약 얻어낸 캐시가 maxAge 보다 오래된 경우, 캐시는 삭제됩니다. (이 타이밍은 커스터마이징 할수 있습니다.)

원하는 타이밍에 캐시를 restore할때 이렇게 쓸수 있습니다.

persistQueryClientRestore({
  queryClient,
  persister,
  maxAge = 1000 * 60 * 60 * 24, // 24 hours
  buster = '',
  hydrateOptions = undefined,
})

persistQueryClient

다음 액션들을 취합니다
1. 모든 persisted cache 를 즉시 restore한다 (see persisteQueryClientRestore)
2. 쿼리 캐시를 구독하고 unsubscribe 함수를 리턴한다. (see persistQueryClientSubscribe)

persistQueryClient({
  queryClient,
  persister,
  maxAge = 1000 * 60 * 60 * 24, // 24 hours
  buster = '',
  hydrateOptions = undefined,
  dehydrateOptions = undefined,
})

Options

interface PersistQueryClientOptions {
  /** The QueryClient to persist */
  queryClient: QueryClient
  /** The Persister interface for storing and restoring the cache
   * to/from a persisted location */
  persister: Persister
  /** maxAge: 최대 허용되는 캐싱 기간 (ms)
   * 해당 시간보다 오래된 persisted 캐시는 스스로 삭제됩니다.
   * (기본값 24시간) */
  maxAge?: number
  /** buster 는 유니크한 문자열. 
   * 만약 같은 buster string 을 갖지 않을 경우 강제로 캐시를 비활성시킵니다. 
   */
  buster?: string
  /** hydrateOptions 는 hydrate 함수에 넘겨집니다.
   * `persistQueryClientSave` 나 `persistQueryClientSubscribe`에 사용X */
  hydrateOptions?: HydrateOptions
  /** dehydrateOptions 는 dehydrate 함수에 넘겨집니다.
  * `persistQueryClientRestore`에 사용X*/
  dehydrateOptions?: DehydrateOptions
}

실제로 사용할 수 있는 인터페이스는 세 가지입니다:

  1. PersistedQueryClientSaveOptions is used for persistQueryClientSave and persistQueryClientSubscribe(doesn't use hydrateOptions).
  2. PersistedQueryClientRestoreOptions is used for persistQueryClientRestore (doesn't use dehydrateOptions).
  3. PersistQueryClientOptions is used for persistQueryClient

리액트에서의 용법

  • persistQueryClient는 캐시 restore을 시도하고 추가 변경사항에 자동으로 subscribe하여 제공된 storage에 클라이언트를 동기화합니다.
  • 하지만, restoring은 비동기적입니다. 모든 persister가 비동기적이기 때문입니다. 즉, restore하는 동안 App을 렌더링하면 쿼리가 mount되고 동시에 fetch해올 경우 경쟁 상태가 될 수 있습니다.
  • 더욱이, 리액트 컴포넌트의 lifecycle 밖에서 변화를 대한 subscribe 를 하고 있으면 unsubscribe 할 수단이 없게됩니다:
// 🚨 절대로 동기 상태를 취소할 수 없습니다 
// never unsubscribes from syncing
persistQueryClient({
  queryClient,
  persister: localStoragePersister,
})

// 🚨 restoring과 동시에 발생합니다
ReactDOM.createRoot(rootElement).render(<App />)

PersistQueryClientProvider

  • PersisteQueryClientProvider 를 사용해서 React 컴포넌트의 수명 주기에 따라 올바르게 subscribe/unsubscribe할 수 있으며, restore 중에도 쿼리 fetch가 시작되지 않습니다.
  • 쿼리가 계속 렌더링하고 있어도, 데이터가 restore가 완료될 때까지 fetchingState: idle 상태가 됩니다.
  • 그런 다음, restored data가 충분히 fresh가 아니면, 다시 refetch하고 initialData도 respect 됩니다.
  • 일반 QueryClientProvider 대신 사용할 수 있습니다:
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      cacheTime: 1000 * 60 * 60 * 24, // 24 hours
    },
  },
})

const persister = createSyncStoragePersister({
  storage: window.localStorage,
})

ReactDOM.createRoot(rootElement).render(
  <PersistQueryClientProvider
    client={queryClient}
    persistOptions={{ persister }}
  >
    <App />
  </PersistQueryClientProvider>
)

props

PersistQueryClientProviderQueryClientProvider 와 같은 props 를 갖고, 추가적으로 다음 props 를 갖습니다.

  • persistOptions: PersistQueryClientOptions : persistQueryClient 에 넘길수 있는 옵션들 (QueryClient 자신은 제외)
  • onSuccess?: () => void

useIsRestoring

  • 당신이 PersistQueryClientProvider를 사용하고 싶으면, useIsRestoring 훅도 함께 사용하여 restore가 현재 진행 중인지 확인할 수도 있습니다.
  • useQuery와 친구들은 restore와 mounting queries 사이의 경합 조건을 피하기 위해 내부적으로 이를 검사합니다.

Persisters

Persister Interface

Persister의 인터페이스

export interface Persister {
  persistClient(persistClient: PersistedClient): Promisable<void>
  restoreClient(): Promisable<PersistedClient | undefined>
  removeClient(): Promisable<void>
}

Persisted Client entries 들은 다음 인터페이스를 갖습니다.

export interface PersistedClient {
  timestamp: number
  buster: string
  cacheState: any
}

persister 를 빌드하기 위해 다음을 import 할 수 있습니다.

import { PersistedClient, Persister } from "@tanstack/react-query-persist-client";

Example - Building a Persister

  • 원하는대로 persist 할 수 있습니다.

indexed DB persister 를 빌드하는 예제

import { get, set, del } from "idb-keyval";
import { PersistedClient, Persister } from "@tanstack/react-query-persist-client";

/**
 * Creates an Indexed DB persister
 * @see https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
 */
export function createIDBPersister(idbValidKey: IDBValidKey = "reactQuery") {
  return {
    persistClient: async (client: PersistedClient) => {
      set(idbValidKey, client);
    },
    restoreClient: async () => {
      return await get<PersistedClient>(idbValidKey);
    },
    removeClient: async () => {
      await del(idbValidKey);
    },
  } as Persister;
}
  • Web Storage API 와 비교해서, Indexted DB가 더 빠르고, 5MB 만큼 더 저장할수 있고, serialization 도 안해도 됩니다. 즉 DateFile 같은 자바스크립트의 native type들을 더 쉽게 저장 할 수 있습니다.

0개의 댓글