Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/reference/QueryCache.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const queryCache = new QueryCache({
},
})

const query = queryCache.find(['posts'])
const query = queryCache.find({ queryKey: ['posts'] })
```

Its available methods are:
Expand Down Expand Up @@ -52,7 +52,7 @@ Its available methods are:
> Note: This is not typically needed for most applications, but can come in handy when needing more information about a query in rare scenarios (eg. Looking at the query.state.dataUpdatedAt timestamp to decide whether a query is fresh enough to be used as an initial value)

```tsx
const query = queryCache.find(queryKey)
const query = queryCache.find({ queryKey })
```

**Options**
Expand All @@ -71,7 +71,7 @@ const query = queryCache.find(queryKey)
> Note: This is not typically needed for most applications, but can come in handy when needing more information about a query in rare scenarios

```tsx
const queries = queryCache.findAll(queryKey)
const queries = queryCache.findAll({ queryKey })
```

**Options**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,23 @@ describe('createPersister', () => {
})
})

describe('retrieveQuery', () => {
it('should return the persisted data when called without a restore callback', async () => {
const storage = getFreshStorage()
const { persister, client, queryHash, queryKey } = setupPersister(
['foo'],
{ storage },
)

client.setQueryData(queryKey, 'baz')
await persister.persistQueryByKey(queryKey, client)

const restoredData = await persister.retrieveQuery(queryHash)

expect(restoredData).toBe('baz')
})
})

describe('persisterGc', () => {
it('should properly clean storage from busted entries', async () => {
const storage = getFreshStorage()
Expand All @@ -542,6 +559,36 @@ describe('createPersister', () => {
await persister.persisterGc()
expect(await storage.entries()).toHaveLength(0)
})

it('should remove entries that cannot be deserialized', async () => {
const storage = getFreshStorage()
const { persister } = setupPersister(['foo'], { storage })

await storage.setItem(`${PERSISTER_KEY_PREFIX}-["foo"]`, 'not-json{')
expect(await storage.entries()).toHaveLength(1)

await persister.persisterGc()
expect(await storage.entries()).toHaveLength(0)
})

it('should keep entries that are neither expired nor busted', async () => {
const storage = getFreshStorage()
const { persister, client, query, queryKey } = setupPersister(['foo'], {
storage,
})
query.setState({
dataUpdatedAt: Date.now(),
data: 'foo',
})
client.getQueryCache().add(query)

await persister.persistQueryByKey(queryKey, client)

expect(await storage.entries()).toHaveLength(1)

await persister.persisterGc()
expect(await storage.entries()).toHaveLength(1)
})
})

describe('restoreQueries', () => {
Expand Down Expand Up @@ -674,6 +721,19 @@ describe('createPersister', () => {
})
expect(client.getQueryCache().getAll()).toHaveLength(1)
})

it('should remove entries that cannot be deserialized', async () => {
const storage = getFreshStorage()
const { persister, client } = setupPersister(['foo'], { storage })

await storage.setItem(`${PERSISTER_KEY_PREFIX}-["foo"]`, 'not-json{')
expect(await storage.entries()).toHaveLength(1)

await persister.restoreQueries(client)

expect(await storage.entries()).toHaveLength(0)
expect(client.getQueryCache().getAll()).toHaveLength(0)
})
})

describe('removeQueries', () => {
Expand Down Expand Up @@ -763,5 +823,17 @@ describe('createPersister', () => {
})
expect(await storage.entries()).toHaveLength(0)
})

it('should remove entries that cannot be deserialized', async () => {
const storage = getFreshStorage()
const { persister } = setupPersister(['foo'], { storage })

await storage.setItem(`${PERSISTER_KEY_PREFIX}-["foo"]`, 'not-json{')
expect(await storage.entries()).toHaveLength(1)

await persister.removeQueries({ queryKey: ['foo'] })

expect(await storage.entries()).toHaveLength(0)
})
})
})
71 changes: 70 additions & 1 deletion packages/query-persist-client-core/src/__tests__/persist.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { QueriesObserver, QueryClient, dehydrate } from '@tanstack/query-core'
import {
MutationObserver,
QueriesObserver,
QueryClient,
dehydrate,
} from '@tanstack/query-core'
import {
persistQueryClient,
persistQueryClientRestore,
persistQueryClientSubscribe,
} from '../persist'
Expand Down Expand Up @@ -68,6 +74,28 @@ describe('persist', () => {

unsubscribe()
})

it('should not be triggered on mutation observer type events', () => {
const persister = createSpyPersister()

const unsubscribe = persistQueryClientSubscribe({
queryClient,
persister,
})

const observer = new MutationObserver(queryClient, {
mutationFn: () => Promise.resolve('data'),
})
const unsubscribeObserver = observer.subscribe(vi.fn())
observer.setOptions({ mutationKey: ['test'] })
unsubscribeObserver()

// Events fired by manipulating the mutation observer are not cache
// events, so they must not trigger a persist.
expect(persister.persistClient).not.toHaveBeenCalled()

unsubscribe()
})
})

describe('persistQueryClientRestore', () => {
Expand Down Expand Up @@ -228,4 +256,45 @@ describe('persist', () => {
expect(persister.removeClient).toHaveBeenCalledTimes(1)
})
})

describe('persistQueryClient', () => {
it('should subscribe to the query cache after a successful restore', async () => {
const persister = createSpyPersister()
persister.restoreClient = () => Promise.resolve(undefined)

const [unsubscribe, restorePromise] = persistQueryClient({
queryClient,
persister,
})
await restorePromise

queryClient.setQueryData(['key'], 'data')

expect(persister.persistClient).toHaveBeenCalled()

unsubscribe()
})

it('should not subscribe when unsubscribed before the restore completes', async () => {
const persister = createSpyPersister()
let resolveRestore: () => void
persister.restoreClient = () =>
new Promise((resolve) => {
resolveRestore = () => resolve(undefined)
})

const [unsubscribe, restorePromise] = persistQueryClient({
queryClient,
persister,
})
// Unsubscribe before the restore resolves
unsubscribe()
resolveRestore!()
await restorePromise

queryClient.setQueryData(['key'], 'data')

expect(persister.persistClient).not.toHaveBeenCalled()
})
})
})
Loading