seitu
Version:
88 lines (87 loc) • 2.33 kB
JavaScript
import { deepEqual } from "fast-equals";
import { readable } from "svelte/store";
//#region src/svelte/hooks/index.ts
/**
* Subscribe to a reactive value from a Svelte component. Pass a handle or a
* factory (run once). Returns a Svelte `Readable` (`$value`); it attaches on
* first subscriber and detaches on last.
*
* @example Inline subscription
* ```svelte
* <script lang="ts">
* import { createWebStorageValue } from 'seitu/web'
* import { useSubscription } from 'seitu/svelte'
* import * as z from 'zod'
*
* const value = useSubscription(() => createWebStorageValue({
* type: 'sessionStorage',
* key: 'test',
* defaultValue: 0,
* schema: z.number(),
* }))
* <\/script>
*
* <div>{$value}</div>
* ```
*
* @example Instance outside of component
* ```svelte
* <script lang="ts">
* import { createWebStorage } from 'seitu/web'
* import { useSubscription } from 'seitu/svelte'
* import * as z from 'zod'
*
* const sessionStorage = createWebStorage({
* type: 'sessionStorage',
* schemas: { count: z.number(), name: z.string() },
* defaultValues: { count: 0, name: '' },
* })
*
* const value = useSubscription(sessionStorage)
* <\/script>
*
* <div>{$value.count}</div>
* ```
*
* @example With selector
* ```svelte
* <script lang="ts">
* import { createWebStorage } from 'seitu/web'
* import { useSubscription } from 'seitu/svelte'
* import * as z from 'zod'
*
* const sessionStorage = createWebStorage({
* type: 'sessionStorage',
* schemas: { count: z.number(), name: z.string() },
* defaultValues: { count: 0, name: '' },
* })
*
* // Updates only when `count` changes
* const count = useSubscription(sessionStorage, { selector: v => v.count })
* <\/script>
*
* <div>{$count}</div>
* ```
*/
function useSubscription(source, options) {
const { selector, isEqual = deepEqual } = options ?? {};
const sub = typeof source === "function" ? source() : source;
function getSnapshot() {
return selector ? selector(sub.get()) : sub.get();
}
const initial = getSnapshot();
return readable(initial, (set) => {
let current = initial;
function refresh() {
const next = getSnapshot();
if (!isEqual(current, next)) {
current = next;
set(next);
}
}
refresh();
return sub.subscribe(refresh);
});
}
//#endregion
export { useSubscription };