https://github.com/patlux/react-query-cache-persistent
Persist your queries in a simple and performant way
https://github.com/patlux/react-query-cache-persistent
cache persistent query react-query
Last synced: 2 months ago
JSON representation
Persist your queries in a simple and performant way
- Host: GitHub
- URL: https://github.com/patlux/react-query-cache-persistent
- Owner: patlux
- License: mit
- Created: 2024-06-21T15:03:45.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2024-08-18T21:41:46.000Z (9 months ago)
- Last Synced: 2025-03-07T01:47:22.654Z (3 months ago)
- Topics: cache, persistent, query, react-query
- Language: TypeScript
- Homepage: https://patwoz.dev
- Size: 101 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# react-query-cache-persistent
This persistor extends [QueryCache](https://tanstack.com/query/v5/docs/reference/QueryCache) to persist the cache in a simple way without hydration/dehydration phase.
The biggest advantage in terms of performance is, that the persistor stores the queries by each single query instead of storing the whole cache on each change.
This is huge if your cache is several megabytes in size.
> ⚠️ The only drawback is that this only works in a synchronous way. You cannot use this if your storage only provides asynchronous methods to get/set the cache. See [How it works](#how-it-works)
## Usage
See [below for examples](#examples).
```ts
import { PersistentQueryCache } from 'react-query-cache-persistent'// Your implementation. See below for examples.
const persistentQueryCache = new PersistentQueryCache({
add: (query) => {},
updated: (query) => {},
removed: (query) => {},
})const queryClient = new QueryClient({ queryCache: persistentQueryCache })
```## Examples
### Web
Uses `localStorage` as storage.

```ts
import { PersistentQueryCache } from 'react-query-cache-persistent'const persistentQueryCache = new PersistentQueryCache({
add: (query) => {
const item = window.localStorage.getItem(`queryCache.${query.queryHash}`)if (item != null) {
query.state = JSON.parse(item)
}window.localStorage.setItem(`queryCache.${query.queryHash}`, JSON.stringify(query.state))
},
updated: (query) => {
window.localStorage.setItem(`queryCache.${query.queryHash}`, JSON.stringify(query.state))
},
removed: (query) => {
window.localStorage.removeItem(`queryCache.${query.queryHash}`)
},
})const queryClient = new QueryClient({ queryCache: persistentQueryCache })
```### React Native
Uses `@op-engineering/op-sqlite` as storage. But you can use any sqlite solution as long it supports synchronous get/set.
The example below writes each query as a table row into a sqlite database

```ts
import {
ANDROID_DATABASE_PATH,
IOS_LIBRARY_PATH,
PreparedStatementObj,
open,
} from '@op-engineering/op-sqlite'
import { Query } from '@tanstack/react-query'
import { Platform } from 'react-native'import { PersistentQueryCache } from 'react-query-cache-persistent'
const tableName = 'queryCache'
const connect = () => {
return open({
name: 'queryCache.sqlite3',
location: Platform.OS === 'ios' ? IOS_LIBRARY_PATH : ANDROID_DATABASE_PATH,
})
}let db = connect()
db.execute(
`CREATE TABLE IF NOT EXISTS ${tableName} (queryHash TEXT NOT NULL UNIQUE, queryState TEXT) STRICT;`
)const selectStmt = db.prepareStatement(`SELECT queryState FROM ${tableName} WHERE queryHash = ?;`)
const insertStmt = db.prepareStatement(
`INSERT INTO ${tableName} (queryHash, queryState) VALUES (?, ?) ON CONFLICT(queryHash) DO UPDATE SET queryState=excluded.queryState;`
)
const deleteStmt = db.prepareStatement(`DELETE FROM ${tableName} WHERE queryHash = ?;`)/**
* Executes the prepared statement with the given parameters
*
* Will retry once if it throws `[OP-SQLite] DB is not open`
*/
const runStmt = (stmt: PreparedStatementObj, params: string[]) => {
try {
stmt.bind(params)
return stmt.execute()
} catch (error: unknown) {
if (`${error}`.includes('[OP-SQLite] DB is not open')) {
// retry once (on iOS the first execution fails on first start, but only in the context of PersistQueryCache::add)
db = connect()
stmt.bind(params)
const result = stmt.execute()
console.warn(`First execution failed. Second try worked.`)
return result
}
console.warn(`Failed to execute query: "${error}".`)
throw error
}
}export const queryCache = new PersistentQueryCache({
add: (query: Query) => {
const result = runStmt(selectStmt, [query.queryHash])const firstRow = result.rows?._array[0]
if (firstRow != null) {
try {
query.state = JSON.parse(firstRow.queryState)
} catch (error: unknown) {
console.warn(`Failed to hydrate state for query "${query.queryHash}": ${error}`)
}
}runStmt(insertStmt, [query.queryHash, JSON.stringify(query.state)])
},updated: (query: Query) => {
runStmt(insertStmt, [query.queryHash, JSON.stringify(query.state)])
},removed: (query: Query) => {
runStmt(deleteStmt, [query.queryHash])
},
})
```# How it works
> TODO
# License
MIT License.