-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
283 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<script lang="ts"> | ||
import { dump } from 'js-yaml' | ||
import { useStorage } from '.' | ||
const state = useStorage('svelte-use-local-storage', { | ||
name: 'Banana', | ||
color: 'Yellow', | ||
size: 'Medium', | ||
}) | ||
</script> | ||
|
||
<input bind:value={$state.name} type="text"> | ||
<input bind:value={$state.color} type="text"> | ||
<input bind:value={$state.size} type="text"> | ||
|
||
<pre lang="json">{ dump($state, { skipInvalid: true }) }</pre> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
--- | ||
category: State | ||
--- | ||
|
||
# useStorage | ||
|
||
Reactive [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)/[SessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) | ||
|
||
## Usage | ||
|
||
```html | ||
<script> | ||
import { useStorage } from '@svelte-use/core' | ||
// bind object | ||
const state = useStorage('my-store', { hello: 'hi', greeting: 'Hello' }) // returns Writable<object> | ||
// bind boolean | ||
const flag = useStorage('my-flag', true) // returns Writable<boolean> | ||
// bind number | ||
const count = useStorage('my-count', 0) // returns Writable<number> | ||
// bind string with SessionStorage | ||
const id = useStorage('my-id', 'some-string-id', sessionStorage) // returns Writable<string> | ||
// delete data from storage | ||
$state = null | ||
</script> | ||
``` | ||
|
||
## Custom Serialization | ||
|
||
By default, `useStorage` will smartly use the corresponding serializer based on the data type of provided default value. For example, `JSON.stringify` / `JSON.parse` will be used for objects, `Number.toString` / `parseFloat` for numbers, etc. | ||
|
||
You can also provide your own serialization function to `useStorage`: | ||
|
||
```html | ||
<script> | ||
import { useStorage } from '@svelte-use/core' | ||
useStorage( | ||
'key', | ||
{}, | ||
{ | ||
serializer: { | ||
read: (v: any) => v ? JSON.parse(v) : null, | ||
write: (v: any) => JSON.stringify(v), | ||
} | ||
} | ||
}) | ||
</script> | ||
``` | ||
|
||
Please note when you provide `null` as the default value, `useStorage` can't assume the data type from it. In this case, you can provide a custom serializer or reuse the built-in ones explicitly. | ||
|
||
```html | ||
<script> | ||
import { useStorage, StorageSerializers } from '@svelte-use/core' | ||
const objectLike = useStorage('key', null, { | ||
serializer: StorageSerializers.object, | ||
}) | ||
$objectLike = { foo: 'bar' } | ||
</script> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,180 @@ | ||
export function useStorage() {} | ||
import { | ||
MaybeWritable, | ||
RemovableWritable, | ||
toWritable, | ||
unstore, | ||
} from '@svelte-use/shared' | ||
import { useEventListener } from '../useEventListener' | ||
import { ConfigurableWindow, defaultWindow } from '../_configurable' | ||
|
||
export type Serializer<T> = { | ||
read(raw: string): T | ||
write(value: T): string | ||
} | ||
|
||
export const StorageSerializers: Record< | ||
'boolean' | 'object' | 'number' | 'string' | 'any', | ||
Serializer<any> | ||
> = { | ||
boolean: { | ||
read: (v: any) => v === 'true', | ||
write: (v: any) => String(v), | ||
}, | ||
object: { | ||
read: (v: any) => JSON.parse(v), | ||
write: (v: any) => JSON.stringify(v), | ||
}, | ||
number: { | ||
read: (v: any) => Number.parseFloat(v), | ||
write: (v: any) => String(v), | ||
}, | ||
string: { | ||
read: (v: any) => v, | ||
write: (v: any) => String(v), | ||
}, | ||
any: { | ||
read: (v: any) => v, | ||
write: (v: any) => String(v), | ||
}, | ||
} | ||
|
||
export type StorageLike = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'> | ||
|
||
export interface StorageOptions<T> extends ConfigurableWindow { | ||
/** | ||
* Listen to storage changes, useful for multiple tabs application | ||
* | ||
* @default true | ||
*/ | ||
listenToStorageChanges?: boolean | ||
|
||
/** | ||
* Custom data serialization | ||
*/ | ||
serializer?: Serializer<T> | ||
|
||
/** | ||
* On error callback | ||
* | ||
* Default lo error to `console.error` | ||
*/ | ||
onError?: (error: unknown) => void | ||
} | ||
|
||
export function useStorage( | ||
key: string, | ||
initialValue: MaybeWritable<string>, | ||
storage?: StorageLike, | ||
options?: StorageOptions<string>, | ||
): RemovableWritable<string> | ||
export function useStorage( | ||
key: string, | ||
initialValue: MaybeWritable<boolean>, | ||
storage?: StorageLike, | ||
options?: StorageOptions<boolean>, | ||
): RemovableWritable<boolean> | ||
export function useStorage( | ||
key: string, | ||
initialValue: MaybeWritable<number>, | ||
storage?: StorageLike, | ||
options?: StorageOptions<number>, | ||
): RemovableWritable<number> | ||
export function useStorage<T>( | ||
key: string, | ||
initialValue: MaybeWritable<T>, | ||
storage?: StorageLike, | ||
options?: StorageOptions<T>, | ||
): RemovableWritable<T> | ||
export function useStorage<T = unknown>( | ||
key: string, | ||
initialValue: MaybeWritable<null>, | ||
storage?: StorageLike, | ||
options?: StorageOptions<T>, | ||
): RemovableWritable<T> | ||
|
||
/** | ||
* Reactive LocalStorage/SessionStorage | ||
* | ||
* @see https://svelte-use.vercel.app/core/useStorage | ||
* @param key | ||
* @param initialValue | ||
* @param storage | ||
* @param options | ||
*/ | ||
export function useStorage<T extends boolean | object | number | string | null>( | ||
key: string, | ||
initialValue: MaybeWritable<T>, | ||
storage: StorageLike | undefined = defaultWindow?.localStorage, | ||
options: StorageOptions<T> = {}, | ||
): RemovableWritable<T> { | ||
const { | ||
listenToStorageChanges = true, | ||
window = defaultWindow, | ||
onError = (e) => { | ||
console.error(e) | ||
}, | ||
} = options | ||
|
||
const rawInit: T = unstore(initialValue) | ||
|
||
// https://github.com/vueuse/vueuse/blob/706a04921c6bb81a5fc3687c2b2748a81f5e9a21/packages/core/useStorage/index.ts#L98 | ||
const type = | ||
rawInit == null | ||
? 'any' | ||
: typeof rawInit === 'boolean' | ||
? 'boolean' | ||
: typeof rawInit === 'string' | ||
? 'string' | ||
: typeof rawInit === 'object' | ||
? 'object' | ||
: Array.isArray(rawInit) | ||
? 'object' | ||
: !Number.isNaN(rawInit) | ||
? 'number' | ||
: 'any' | ||
|
||
const data = toWritable(initialValue) as RemovableWritable<T> | ||
const serializer = options.serializer ?? StorageSerializers[type] | ||
|
||
function read(event?: StorageEvent) { | ||
if (!storage || (event && event.key !== key)) { | ||
return | ||
} | ||
|
||
try { | ||
const rawValue = event ? event.newValue : storage.getItem(key) | ||
if (rawValue == null) { | ||
data.set(rawInit) | ||
if (rawInit !== null) { | ||
storage.setItem(key, serializer.write(rawInit)) | ||
} | ||
} else { | ||
data.set(serializer.read(rawValue)) | ||
} | ||
} catch (e) { | ||
onError(e) | ||
} | ||
} | ||
|
||
read() | ||
|
||
if (window && listenToStorageChanges) { | ||
useEventListener(window, 'storage', read) | ||
} | ||
|
||
if (storage) { | ||
data.subscribe((value) => { | ||
try { | ||
if (value == null) { | ||
storage.removeItem(key) | ||
} else { | ||
storage.setItem(key, serializer.write(value)) | ||
} | ||
} catch (e) { | ||
onError(e) | ||
} | ||
}) | ||
} | ||
|
||
return data | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters