/
index.ts
126 lines (114 loc) · 4.04 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import type { MaybeRefOrGetter, RemovableRef } from '@vueuse/shared'
import { toValue, watchWithFilter } from '@vueuse/shared'
import type { Ref } from 'vue-demi'
import { ref, shallowRef } from 'vue-demi'
import type { StorageLikeAsync } from '../ssr-handlers'
import { getSSRHandler } from '../ssr-handlers'
import type { SerializerAsync, UseStorageOptions } from '../useStorage'
import { StorageSerializers } from '../useStorage'
import { useEventListener } from '../useEventListener'
import { guessSerializerType } from '../useStorage/guess'
import { defaultWindow } from '../_configurable'
export interface UseStorageAsyncOptions<T> extends Omit<UseStorageOptions<T>, 'serializer'> {
/**
* Custom data serialization
*/
serializer?: SerializerAsync<T>
}
export function useStorageAsync(key: string, initialValue: MaybeRefOrGetter<string>, storage?: StorageLikeAsync, options?: UseStorageAsyncOptions<string>): RemovableRef<string>
export function useStorageAsync(key: string, initialValue: MaybeRefOrGetter<boolean>, storage?: StorageLikeAsync, options?: UseStorageAsyncOptions<boolean>): RemovableRef<boolean>
export function useStorageAsync(key: string, initialValue: MaybeRefOrGetter<number>, storage?: StorageLikeAsync, options?: UseStorageAsyncOptions<number>): RemovableRef<number>
export function useStorageAsync<T>(key: string, initialValue: MaybeRefOrGetter<T>, storage?: StorageLikeAsync, options?: UseStorageAsyncOptions<T>): RemovableRef<T>
export function useStorageAsync<T = unknown>(key: string, initialValue: MaybeRefOrGetter<null>, storage?: StorageLikeAsync, options?: UseStorageAsyncOptions<T>): RemovableRef<T>
/**
* Reactive Storage in with async support.
*
* @see https://vueuse.org/useStorageAsync
* @param key
* @param initialValue
* @param storage
* @param options
*/
export function useStorageAsync<T extends(string | number | boolean | object | null)>(
key: string,
initialValue: MaybeRefOrGetter<T>,
storage: StorageLikeAsync | undefined,
options: UseStorageAsyncOptions<T> = {},
): RemovableRef<T> {
const {
flush = 'pre',
deep = true,
listenToStorageChanges = true,
writeDefaults = true,
mergeDefaults = false,
shallow,
window = defaultWindow,
eventFilter,
onError = (e) => {
console.error(e)
},
} = options
const rawInit: T = toValue(initialValue)
const type = guessSerializerType<T>(rawInit)
const data = (shallow ? shallowRef : ref)(initialValue) as Ref<T>
const serializer = options.serializer ?? StorageSerializers[type]
if (!storage) {
try {
storage = getSSRHandler('getDefaultStorageAsync', () => defaultWindow?.localStorage)()
}
catch (e) {
onError(e)
}
}
async function read(event?: StorageEvent) {
if (!storage || (event && event.key !== key))
return
try {
const rawValue = event ? event.newValue : await storage.getItem(key)
if (rawValue == null) {
data.value = rawInit
if (writeDefaults && rawInit !== null)
await storage.setItem(key, await serializer.write(rawInit))
}
else if (mergeDefaults) {
const value = await serializer.read(rawValue)
if (typeof mergeDefaults === 'function')
data.value = mergeDefaults(value, rawInit)
else if (type === 'object' && !Array.isArray(value))
data.value = { ...(rawInit as any), ...value }
else data.value = value
}
else {
data.value = await serializer.read(rawValue)
}
}
catch (e) {
onError(e)
}
}
read()
if (window && listenToStorageChanges)
useEventListener(window, 'storage', e => Promise.resolve().then(() => read(e)))
if (storage) {
watchWithFilter(
data,
async () => {
try {
if (data.value == null)
await storage!.removeItem(key)
else
await storage!.setItem(key, await serializer.write(data.value))
}
catch (e) {
onError(e)
}
},
{
flush,
deep,
eventFilter,
},
)
}
return data as RemovableRef<T>
}