diff --git a/packages/reactivity/__tests__/ref.spec.ts b/packages/reactivity/__tests__/ref.spec.ts index 6620f7fd9ce..9ebe8f46ea6 100644 --- a/packages/reactivity/__tests__/ref.spec.ts +++ b/packages/reactivity/__tests__/ref.spec.ts @@ -8,7 +8,7 @@ import { isReactive } from '../src/index' import { computed } from '@vue/runtime-dom' -import { shallowRef, unref } from '../src/ref' +import { shallowRef, unref, customRef } from '../src/ref' describe('reactivity/ref', () => { it('should hold a value', () => { @@ -208,4 +208,33 @@ describe('reactivity/ref', () => { expect(dummyX).toBe(4) expect(dummyY).toBe(5) }) + + test('customRef', () => { + let value = 1 + let _trigger: () => void + + const custom = customRef((track, trigger) => ({ + get() { + track() + return value + }, + set(newValue: number) { + value = newValue + _trigger = trigger + } + })) + + let dummy + effect(() => { + dummy = custom.value + }) + expect(dummy).toBe(1) + + custom.value = 2 + // should not trigger yet + expect(dummy).toBe(1) + + _trigger!() + expect(dummy).toBe(2) + }) }) diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index 91b1829e0de..d83b0f30585 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -1,4 +1,13 @@ -export { ref, unref, shallowRef, isRef, toRefs, Ref, UnwrapRef } from './ref' +export { + ref, + unref, + shallowRef, + isRef, + toRefs, + customRef, + Ref, + UnwrapRef +} from './ref' export { reactive, isReactive, diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 05304ad7ef9..bbbd94841a3 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -70,6 +70,31 @@ export function unref(ref: T): T extends Ref ? V : T { return isRef(ref) ? (ref.value as any) : ref } +export type CustomRefFactory = ( + track: () => void, + trigger: () => void +) => { + get: () => T + set: (value: T) => void +} + +export function customRef(factory: CustomRefFactory): Ref { + const { get, set } = factory( + () => track(r, TrackOpTypes.GET, 'value'), + () => trigger(r, TriggerOpTypes.SET, 'value') + ) + const r = { + _isRef: true, + get value() { + return get() + }, + set value(v) { + set(v) + } + } + return r as any +} + export function toRefs( object: T ): { [K in keyof T]: Ref } { diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 334b7c6f3b5..2b4923e2323 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -7,13 +7,14 @@ export { shallowRef, isRef, toRefs, + customRef, reactive, isReactive, readonly, isReadonly, shallowReactive, - toRaw, - markNonReactive + markNonReactive, + toRaw } from '@vue/reactivity' export { computed } from './apiComputed' export { watch, watchEffect } from './apiWatch'