Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/piotr-cz/swr-idb-cache
IndexedDB Cache Provider for SWR
https://github.com/piotr-cz/swr-idb-cache
Last synced: 12 days ago
JSON representation
IndexedDB Cache Provider for SWR
- Host: GitHub
- URL: https://github.com/piotr-cz/swr-idb-cache
- Owner: piotr-cz
- License: mit
- Created: 2022-01-27T09:44:06.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2024-08-16T12:52:49.000Z (3 months ago)
- Last Synced: 2024-10-12T23:55:07.916Z (26 days ago)
- Language: TypeScript
- Size: 214 KB
- Stars: 22
- Watchers: 3
- Forks: 5
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# IndexedDB Cache Provider for [SWR](https://swr.vercel.app/)
Synchronize SWR Cache with [IndexedDB](https://developer.mozilla.org/en-US/docs/Glossary/IndexedDB) to get offline cache.
Please note that the cache provider initialization process is asynchronous and until it's resolved, best practise is to render fallback component.
> [!WARNING]
> When passing secrets such as an API token to fetcher ([example](https://swr.vercel.app/docs/arguments)),
> it will be saved in the persistent cache as a part of key generated by [stable-hash](https://github.com/shuding/stable-hash).
>
> To overcome this, you may use custom comparision function in SWR Config that replaces secret by it's checksum.## How does it work?
Library reads current state of cache stored in [IndexedDB](https://developer.mozilla.org/en-US/docs/Glossary/IndexedDB) into memory using [idb](https://github.com/jakearchibald/idb) during it's initialization.
Then it resolves into Cache Provider which should be passed to SWR.Read SWR Docs > [Cache](https://swr.vercel.app/docs/advanced/cache) if your are interested in more information about implementation details.
## Installation
Using npm:
```console
npm install --save @piotr-cz/swr-idb-cache
```or Yarn:
```console
yarn add @piotr-cz/swr-idb-cache```
## Requirements
- [SWR](https://swr.vercel.app/) ^2.0.0
*Note: For SWR 1.x use the 1.0.0-rc.2 version of this package*
- Works with [React](https://reactjs.org/) ^16.11 and [Preact](https://preactjs.com/)## Setup
Initialize library and when it's ready, pass it in configuration under `provider` key to SWR.
To wait until provider is resolved, use bundled `useCacheProvider` hook:
```jsx
// App.jsx
import { SWRConfig } from 'swr'
import { useCacheProvider } from '@piotr-cz/swr-idb-cache'function App() {
// Initialize
const cacheProvider = useCacheProvider({
dbName: 'my-app',
storeName: 'swr-cache',
})// Cache Provider is being initialized - render fallback component in the meantime
if (!cacheProvider) {
returnInitializing cache…
}return (
)
}
```…or library like [react-use-promise](https://github.com/bsonntag/react-use-promise):
```js
import createCacheProvider from '@piotr-cz/swr-idb-cache'
import usePromise from 'react-use-promise'function App() {
// Initialize
const [ cacheProvider ] = usePromise(() => createCacheProvider({
dbName: 'my-app',
storeName: 'swr-cache',
}), [])// …
}```
## Configuration
- `dbName`: IndexedDB Database name
- `storeName`: IndexedDB Store name
- `storageHandler` (optional): Custom Storage handler, see [IStorageHandler interface](./src/types.ts#L31)
- `version` (optional): Schema version, defaults to `1`
- `onError` (optional): Database error handler, defaults to noop functionNote: When using `useCacheProvider`, changing options doesn't create new cache provider.
## Known issues
### InvalidStateError
```
InvalidStateError
Failed to execute 'transaction' on 'IDBDatabase': The database connection is closing.
```See idb [Issue #229](https://github.com/jakearchibald/idb/issues/229)
## Recipes
### Delete cache entry
```jsx
import useSWR, { useSWRConfig } from 'swr'export default function Item() {
const { data, error } = useSWR('/api/data')
const { cache } = useSWRConfig()const handleRemove = () => {
// Remove from state
// …// Remove from cache with key used in useSWR hook
cache.delete('/api/data')
}return (
{/** Show item */}
{data &&
{data.label}
}{/** Remove item */}
Remove
)
}
```### Implement Garbage Collector
Define custom storage handler that extends timestamp storage
```js
// custom-storage-handler.js
import { timestampStorageHandler } from '@piotr-cz/swr-idb-cache'// Define max age of 7 days
const maxAge = 7 * 24 * 60 * 60 * 1e3const gcStorageHandler = {
...timestampStorageHandler,
// Revive each entry only when it's timestamp is newer than expiration
revive: (key, storeObject) =>
storeObject.ts > Date.now() - maxAge
// Unwrapped value
? timestampStorageHandler.revive(key, storeObject)
// Undefined to indicate item is stale
: undefined,
}export default gcStorageHandler
```Pass it to configuration
```diff
// App.jsx
import { SWRConfig } from 'swr'
import { useCacheProvider } from '@piotr-cz/swr-idb-cache'+import customStorageHandler from './custom-storage-handler.js'
+
function App() {
// Initialize
const cacheProvider = useCacheProvider({
dbName: 'my-app',
storeName: 'swr-cache',
+ storageHandler: customStorageHandler,
})// …
```### Ignore API endpoints from cache persistence
Define custom storage handler that extends timestamp storage
```js
// custom-storage-handler.js
import { timestampStorageHandler } from '@piotr-cz/swr-idb-cache'const blacklistStorageHandler = {
...timestampStorageHandler,
// Ignore entries fetched from API endpoints starting with /api/device
replace: (key, value) =>
!key.startsWith('/api/device/')
// Wrapped value
? timestampStorageHandler.replace(key, value)
// Undefined to ignore storing value
: undefined,
}export default blacklistStorageHandler
```Pass it in configuration as in the [recipe above](#implement-garbage-collector)