UNPKG

seitu

Version:
84 lines (83 loc) • 2.26 kB
import { deepEqual } from "fast-equals"; import { computed, onWatcherCleanup, shallowReadonly, shallowRef, toValue, watch } from "vue"; //#region src/vue/composables/index.ts /** * Subscribe to a reactive value. Pass a handle or a ref/getter that returns one. * * @example Inline subscription * ```vue * <script setup lang="ts"> * import { createWebStorageValue } from 'seitu/web' * import { useSubscription } from 'seitu/vue' * import * as z from 'zod' * * const value = useSubscription( * createWebStorageValue({ type: 'sessionStorage', key: 'test', defaultValue: 0, schema: z.number() }), * ) * <\/script> * * <template> * <div>{{ value }}</div> * </template> * ``` * * @example Instance outside of the subscription * ```vue * <script setup lang="ts"> * import { createWebStorage } from 'seitu/web' * import { useSubscription } from 'seitu/vue' * 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> * * <template> * <div>{{ value.count }}</div> * </template> * ``` * * @example With selector * ```vue * <script setup lang="ts"> * import { createWebStorage } from 'seitu/web' * import { useSubscription } from 'seitu/vue' * import * as z from 'zod' * * const sessionStorage = createWebStorage({ * type: 'sessionStorage', * schemas: { count: z.number(), name: z.string() }, * defaultValues: { count: 0, name: '' }, * }) * * const count = useSubscription(sessionStorage, { selector: v => v.count }) * <\/script> * * <template> * <div>{{ count }}</div> * </template> * ``` */ function useSubscription(source, options) { const { selector, isEqual = deepEqual } = options ?? {}; function getSnapshot(sub) { return selector ? selector(sub.get()) : sub.get(); } const sub = computed(() => toValue(source)); const state = shallowRef(); watch(sub, (sub) => { state.value = getSnapshot(sub); onWatcherCleanup(sub.subscribe(() => { const next = getSnapshot(sub); if (!isEqual(state.value, next)) state.value = next; })); }, { immediate: true }); return shallowReadonly(state); } //#endregion export { useSubscription };