diff --git a/docs/framework/react/plugins/createPersister.md b/docs/framework/react/plugins/createPersister.md index 5bd71b1e6b..58ae5d7aae 100644 --- a/docs/framework/react/plugins/createPersister.md +++ b/docs/framework/react/plugins/createPersister.md @@ -1,6 +1,6 @@ --- id: createPersister -title: experimental_createPersister +title: experimental_createQueryPersister --- ## Installation @@ -33,9 +33,9 @@ bun add @tanstack/query-persist-client-core ## Usage -- Import the `experimental_createPersister` function -- Create a new `experimental_createPersister` - - you can pass any `storage` to it that adheres to the `AsyncStorage` or `Storage` interface - the example below uses the async-storage from React Native. +- Import the `experimental_createQueryPersister` function +- Create a new `experimental_createQueryPersister` + - you can pass any `storage` to it that adheres to the `AsyncStorage` interface - the example below uses the async-storage from React Native. - Pass that `persister` as an option to your Query. This can be done either by passing it to the `defaultOptions` of the `QueryClient` or to any `useQuery` hook instance. - If you pass this `persister` as `defaultOptions`, all queries will be persisted to the provided `storage`. You can additionally narrow this down by passing `filters`. In contrast to the `persistClient` plugin, this will not persist the whole query client as a single item, but each query separately. As a key, the query hash is used. - If you provide this `persister` to a single `useQuery` hook, only this Query will be persisted. @@ -48,16 +48,18 @@ Garbage collecting a Query from memory **does not** affect the persisted data. T ```tsx import AsyncStorage from '@react-native-async-storage/async-storage' import { QueryClient } from '@tanstack/react-query' -import { experimental_createPersister } from '@tanstack/query-persist-client-core' +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: experimental_createPersister({ - storage: AsyncStorage, - maxAge: 1000 * 60 * 60 * 12, // 12 hours - }), + persister: persister.persisterFn, }, }, }) @@ -67,12 +69,62 @@ const queryClient = new QueryClient({ The `createPersister` plugin technically wraps the `queryFn`, so it doesn't restore if the `queryFn` doesn't run. In that way, it acts as a caching layer between the Query and the network. Thus, the `networkMode` defaults to `'offlineFirst'` when a persister is used, so that restoring from the persistent storage can also happen even if there is no network connection. +## Additional utilities + +Invoking `experimental_createQueryPersister` returns additional utilities in addition to `persisterFn` for easier implementation of userland functionalities. + +### `persistQueryByKey(queryKey: QueryKey, queryClient: QueryClient): Promise` + +This function will persist `Query` to storage and key defined when creating persister. +This utility might be used along `setQueryData` to persist optimistic update to storage without waiting for invalidation. + +```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) + ... + }, +}) +``` + +### `retrieveQuery(queryHash: string): Promise` + +This function would attempt to retrieve persisted query by `queryHash`. +If `query` is `expired`, `busted` or `malformed` it would be removed from the storage instead, and `undefined` would be returned. + +### `persisterGc(): Promise` + +This function can be used to sporadically clean up stoage from `expired`, `busted` or `malformed` entries. + +For this function to work, your storage must expose `entries` method that would return a `key-value tuple array`. +For example `Object.entries(localStorage)` for `localStorage` or `entries` from `idb-keyval`. + +### `persisterRestoreAll(queryClient: QueryClient): Promise` + +This function can be used to restore all queries that are currently stored by persister in one go. +For example when your app is starting up in offline mode, or you want data from previous session to be immediately available without intermediate `loading` state. + +For this function to work, your storage must expose `entries` method that would return a `key-value tuple array`. +For example `Object.entries(localStorage)` for `localStorage` or `entries` from `idb-keyval`. + ## API -### `experimental_createPersister` +### `experimental_createQueryPersister` ```tsx -experimental_createPersister(options: StoragePersisterOptions) +experimental_createQueryPersister(options: StoragePersisterOptions) ``` #### `Options` @@ -116,10 +168,11 @@ export interface StoragePersisterOptions { filters?: QueryFilters } -interface AsyncStorage { - getItem: (key: string) => Promise - setItem: (key: string, value: string) => Promise - removeItem: (key: string) => Promise +interface AsyncStorage { + getItem: (key: string) => MaybePromise + setItem: (key: string, value: TStorageValue) => MaybePromise + removeItem: (key: string) => MaybePromise + entries?: () => MaybePromise> } ``` diff --git a/docs/framework/vue/plugins/createPersister.md b/docs/framework/vue/plugins/createPersister.md index b2f9773ca8..02fb5f0868 100644 --- a/docs/framework/vue/plugins/createPersister.md +++ b/docs/framework/vue/plugins/createPersister.md @@ -1,6 +1,6 @@ --- id: createPersister -title: experimental_createPersister +title: experimental_createQueryPersister --- ## Installation @@ -31,8 +31,8 @@ bun add @tanstack/query-persist-client-core ## Usage -- Import the `experimental_createPersister` function -- Create a new `experimental_createPersister` +- Import the `experimental_createQueryPersister` function +- Create a new `experimental_createQueryPersister` - you can pass any `storage` to it that adheres to the `AsyncStorage` or `Storage` interface - Pass that `persister` as an option to your Query. This can be done either by passing it to the `defaultOptions` of the `QueryClient` or to any `useQuery` hook instance. - If you pass this `persister` as `defaultOptions`, all queries will be persisted to the provided `storage`. You can additionally narrow this down by passing `filters`. In contrast to the `persistClient` plugin, this will not persist the whole query client as a single item, but each query separately. As a key, the query hash is used. @@ -44,16 +44,18 @@ Garbage collecting a Query from memory **does not** affect the persisted data. T ```tsx import { QueryClient } from '@tanstack/vue-query' -import { experimental_createPersister } from '@tanstack/query-persist-client-core' +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: experimental_createPersister({ - storage: localStorage, - maxAge: 1000 * 60 * 60 * 12, // 12 hours - }), + persister: persister.persisterFn, }, }, }) @@ -65,10 +67,10 @@ The `createPersister` plugin technically wraps the `queryFn`, so it doesn't rest ## API -### `experimental_createPersister` +### `experimental_createQueryPersister` ```tsx -experimental_createPersister(options: StoragePersisterOptions) +experimental_createQueryPersister(options: StoragePersisterOptions) ``` #### `Options` diff --git a/examples/vue/persister/src/Post.vue b/examples/vue/persister/src/Post.vue index 43238f97cf..88d8267035 100644 --- a/examples/vue/persister/src/Post.vue +++ b/examples/vue/persister/src/Post.vue @@ -4,7 +4,7 @@ import { defineComponent } from 'vue' import { useQuery } from '@tanstack/vue-query' import { Post } from './types' -import { experimental_createPersister } from '@tanstack/query-persist-client-core' +import { experimental_createQueryPersister } from '@tanstack/query-persist-client-core' const fetcher = async (id: number): Promise => await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`).then( @@ -24,13 +24,13 @@ export default defineComponent({ const { isPending, isError, isFetching, data, error } = useQuery({ queryKey: ['post', props.postId] as const, queryFn: () => fetcher(props.postId), - persister: experimental_createPersister({ + persister: experimental_createQueryPersister({ storage: { getItem: (key: string) => get(key), setItem: (key: string, value: string) => set(key, value), removeItem: (key: string) => del(key), }, - }), + }).persisterFn, }) return { isPending, isError, isFetching, data, error } diff --git a/examples/vue/persister/src/Posts.vue b/examples/vue/persister/src/Posts.vue index 7aa2c02c5b..505a425e27 100644 --- a/examples/vue/persister/src/Posts.vue +++ b/examples/vue/persister/src/Posts.vue @@ -1,7 +1,7 @@