diff --git a/packages/vue/src/composables/useSyncRefs/index.test.ts b/packages/vue/src/composables/useSyncRefs/index.test.ts new file mode 100644 index 0000000..e04e42c --- /dev/null +++ b/packages/vue/src/composables/useSyncRefs/index.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from 'vitest'; +import { ref } from 'vue'; +import { useSyncRefs } from '.'; + +describe('useSyncRefs', () => { + it('sync the value of a source ref with multiple target refs', () => { + const source = ref(0); + const target1 = ref(0); + const target2 = ref(0); + useSyncRefs(source, [target1, target2]); + + source.value = 10; + + expect(target1.value).toBe(10); + expect(target2.value).toBe(10); + }); + + it('sync the value of a source ref with a single target ref', () => { + const source = ref(0); + const target = ref(0); + useSyncRefs(source, target); + + source.value = 20; + + expect(target.value).toBe(20); + }); + + it('stop watching when the stop handle is called', () => { + const source = ref(0); + const target = ref(0); + const stop = useSyncRefs(source, target); + + source.value = 30; + stop(); + source.value = 40; + + expect(target.value).toBe(30); + }); +}); \ No newline at end of file diff --git a/packages/vue/src/composables/useSyncRefs/index.ts b/packages/vue/src/composables/useSyncRefs/index.ts new file mode 100644 index 0000000..1023097 --- /dev/null +++ b/packages/vue/src/composables/useSyncRefs/index.ts @@ -0,0 +1,44 @@ +import { watch, type Ref, type WatchOptions, type WatchSource } from 'vue'; +import { isArray } from '@robonen/stdlib'; + +/** + * @name useSyncRefs + * @category Reactivity + * @description Syncs the value of a source ref with multiple target refs + * + * @param {WatchSource} source Source ref to sync + * @param {Ref | Ref[]} targets Target refs to sync + * @param {WatchOptions} watchOptions Watch options + * @returns {WatchStopHandle} Watch stop handle + * + * @example + * const source = ref(0); + * const target1 = ref(0); + * const target2 = ref(0); + * useSyncRefs(source, [target1, target2]); + * + * @example + * const source = ref(0); + * const target1 = ref(0); + * useSyncRefs(source, target1, { immediate: true }); + */ +export function useSyncRefs( + source: WatchSource, + targets: Ref | Ref[], + watchOptions: WatchOptions = {}, +) { + const { + flush = 'sync', + deep = false, + immediate = true, + } = watchOptions; + + if (!isArray(targets)) + targets = [targets]; + + return watch( + source, + (value) => targets.forEach((target) => target.value = value), + { flush, deep, immediate }, + ); +}