Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(useIDBKeyval): new integration - Idb-keyval wrapper #2335

Merged
merged 8 commits into from Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/add-ons.md
Expand Up @@ -77,6 +77,7 @@ Integration wrappers for utility libraries
- [`useDrauu`](https://vueuse.org/integrations/useDrauu/) — reactive instance for [drauu](https://github.com/antfu/drauu)
- [`useFocusTrap`](https://vueuse.org/integrations/useFocusTrap/) — reactive wrapper for [`focus-trap`](https://github.com/focus-trap/focus-trap)
- [`useFuse`](https://vueuse.org/integrations/useFuse/) — easily implement fuzzy search using a composable with [Fuse.js](https://github.com/krisk/fuse)
- [`useIDBKeyval`](https://vueuse.org/integrations/useIDBKeyval/) — wrapper for [`idb-keyval`](https://www.npmjs.com/package/idb-keyval)
- [`useJwt`](https://vueuse.org/integrations/useJwt/) — wrapper for [`jwt-decode`](https://github.com/auth0/jwt-decode)
- [`useNProgress`](https://vueuse.org/integrations/useNProgress/) — reactive wrapper for [`nprogress`](https://github.com/rstacruz/nprogress)
- [`useQRCode`](https://vueuse.org/integrations/useQRCode/) — wrapper for [`qrcode`](https://github.com/soldair/node-qrcode)
Expand Down
1 change: 1 addition & 0 deletions packages/integrations/README.md
Expand Up @@ -21,6 +21,7 @@ npm i <b>@vueuse/integrations</b>
- [`useDrauu`](https://vueuse.org/integrations/useDrauu/) — reactive instance for [drauu](https://github.com/antfu/drauu)
- [`useFocusTrap`](https://vueuse.org/integrations/useFocusTrap/) — reactive wrapper for [`focus-trap`](https://github.com/focus-trap/focus-trap)
- [`useFuse`](https://vueuse.org/integrations/useFuse/) — easily implement fuzzy search using a composable with [Fuse.js](https://github.com/krisk/fuse)
- [`useIDBKeyval`](https://vueuse.org/integrations/useIDBKeyval/) — wrapper for [`idb-keyval`](https://www.npmjs.com/package/idb-keyval)
- [`useJwt`](https://vueuse.org/integrations/useJwt/) — wrapper for [`jwt-decode`](https://github.com/auth0/jwt-decode)
- [`useNProgress`](https://vueuse.org/integrations/useNProgress/) — reactive wrapper for [`nprogress`](https://github.com/rstacruz/nprogress)
- [`useQRCode`](https://vueuse.org/integrations/useQRCode/) — wrapper for [`qrcode`](https://github.com/soldair/node-qrcode)
Expand Down
1 change: 1 addition & 0 deletions packages/integrations/index.ts
Expand Up @@ -5,6 +5,7 @@ export * from './useCookies'
export * from './useDrauu'
export * from './useFocusTrap'
export * from './useFuse'
export * from './useIDBKeyval'
export * from './useJwt'
export * from './useNProgress'
export * from './useQRCode'
6 changes: 6 additions & 0 deletions packages/integrations/package.json
Expand Up @@ -86,6 +86,11 @@
"types": "./useAsyncValidator/component.d.ts",
"require": "./useAsyncValidator/component.cjs",
"import": "./useAsyncValidator/component.mjs"
},
"./useIDBKeyval": {
"types": "./useIDBKeyval.d.ts",
"require": "./useIDBKeyval.cjs",
"import": "./useIDBKeyval.mjs"
}
},
"main": "./index.cjs",
Expand Down Expand Up @@ -151,6 +156,7 @@
"drauu": "^0.3.2",
"focus-trap": "^7.0.0",
"fuse.js": "^6.6.2",
"idb-keyval": "^6.2.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also requires to be added to peer deps

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added to peer deps

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sxzz Regarding the first comment, thank you for your patience as I looked into this.
I looked through useLocalStorage and useSessionStorage to understand how they reuse useStorage. I believe you are suggesting that we could use indexDB storage in place of window?.localStorage, the problem I see is that useStorage converts all values to strings, and a big reason our team wanted to use indexDB is so we can store values other than just strings.
I also took a closer look at StorageLike, its get and set functions get and return strings, so I am not sure that using indexDB in this way would provide any gains to using localStorage as is.
Please let me know if I am misunderstanding something.

"jwt-decode": "^3.1.2",
"nprogress": "^0.2.0",
"qrcode": "^1.5.1",
Expand Down
24 changes: 24 additions & 0 deletions packages/integrations/useIDBKeyval/demo.vue
@@ -0,0 +1,24 @@
<script setup lang="ts">
import { stringify } from '@vueuse/docs-utils'
import { useIDBKeyval } from '@vueuse/integrations'

const state = useIDBKeyval('vue-use-idb-keyval', {
name: 'Banana',
color: 'Yellow',
size: 'Medium',
count: 0,
})

const text = stringify(state)
</script>

<template>
<div>
<input v-model="state.name" type="text">
<input v-model="state.color" type="text">
<input v-model="state.size" type="text">
<input v-model.number="state.count" type="range" min="0" step="0.01" max="1000">

<pre lang="json">{{ text }}</pre>
</div>
Harmony222 marked this conversation as resolved.
Show resolved Hide resolved
</template>
35 changes: 35 additions & 0 deletions packages/integrations/useIDBKeyval/index.md
@@ -0,0 +1,35 @@
---
category: '@Integrations'
---

# useIDBKeyval

Wrapper for [`idb-keyval`](https://www.npmjs.com/package/idb-keyval).


## Install idb-keyval as a peer dependency

```bash
npm install idb-keyval
```

## Usage

```ts
import { useIDBKeyval } from '@vueuse/integrations'

// bind object
const storedObject = useIDBKeyval('my-idb-keyval-store', { hello: 'hi', greeting: 'Hello' })

// update object
storedObject.value.hello = 'hola'

// bind boolean
const flag = useIDBKeyval('my-flag', true) // returns Ref<boolean>

// bind number
const count = useIDBKeyval('my-count', 0) // returns Ref<number>

// delete data from idb storage
storedObject.value = null
```
87 changes: 87 additions & 0 deletions packages/integrations/useIDBKeyval/index.ts
@@ -0,0 +1,87 @@
import type { ConfigurableFlush, MaybeComputedRef, RemovableRef } from '@vueuse/shared'
import { resolveUnref } from '@vueuse/shared'
import type { Ref } from 'vue-demi'
import { ref, shallowRef, watch } from 'vue-demi'
import { del, get, set, update } from 'idb-keyval'

export interface UseIDBOptions extends ConfigurableFlush {
/**
* Watch for deep changes
*
* @default true
*/
deep?: boolean

/**
* On error callback
*
* Default log error to `console.error`
*/
onError?: (error: unknown) => void

/**
* Use shallow ref as reference
*
* @default false
*/
shallow?: boolean
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this option and the associated code by having the user pass in a computed, ref, or shallowRef?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following the same pattern as useStorage and useStorageAsync with the shallowRef here, but can change that if needed.

}

/**
*
* @param key
* @param initialValue
* @param options
*/
export function useIDBKeyval<T>(
key: IDBValidKey,
initialValue: MaybeComputedRef<T>,
options: UseIDBOptions = {},
): RemovableRef<T> {
const {
flush = 'pre',
deep = true,
shallow,
onError = (e) => {
console.error(e)
},
} = options

const data = (shallow ? shallowRef : ref)(initialValue) as Ref<T>

const rawInit: T = resolveUnref(initialValue)

async function read() {
try {
const rawValue = await get<T>(key)
if (rawValue === undefined) {
if (rawInit)
set(key, rawInit)
}
else {
data.value = rawValue
}
}
catch (e) {
onError(e)
}
}

read()

async function write() {
try {
if (data.value == null)
await del(key)
else
await update(key, () => ({ ...data.value }))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question, did this have the same issue that you were messaging me about? The issue with ...someString etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah looks like that one is having an issue too. This is what jimmy and I added when we worked through the bug the other day, it had something to do with proxy objects not able to be stored in index db if I remember correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed this logic to only make copy with spread syntax if data.value is an object or array

}
catch (e) {
onError(e)
}
}

watch(data, () => write(), { flush, deep })

return data as RemovableRef<T>
}
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.