extendedRef
Extends a ref with additional properties.
TIP
Before proceeding with extendedRef, learn first about entangled.
Usage
import { extendedRef, markDescriptor } from '@8ctavio/vergil'
const extended = extendedRef(0, { extra1: 0 })
// normally read and write inner reactive value
extended.value = extended.value + 8
// read and write extended properties
extended.extra1 = extended.extra1 + 1
// create and configure properties
extended.extend({
extra2: 2,
extra3: markDescriptor({
value: 3,
enumerable: false,
writable: false
}),
extra4: markDescriptor({
value: ref(4),
unwrap: false
})
})Description
The extendedRef function creates and returns an extendedRef object. In practice, as the name suggests, an extendedRef behaves similar to a regular ref with additional properties.
An extendedRef, however, is not a modified ref object, but an entangled object with additional features to emulate a regular ref. In particular, extendedRefs include a ref property with a regular ref (the ref to be "extended") whose inner reactive value can be read and written through extendedRefs' value property accessors.
const extended = extendedRef()
console.log(isRef(extended)) // false
console.log(isRef(extended.ref)) // true
extended.value = extended.value
// same as
extended.ref.value = extended.ref.valueThe extendedRef function accepts three parameters: value, extension, and options. The value parameter is converted to a ref with toRef, and becomes the created extendedRef's ref property value. On the other hand, extension and options are used to define extendedRef properties, and correspond to the entangled objects' extend method parameters (see property definition).
In addition, get and set properties may be provided to the extendedRef's options object in order to define custom getter and setter functions for an extendedRef's value property.
const extended = extendedRef(undefined, null, {
get() {
console.log('custom getter')
},
set(value) {
console.log('custom setter')
}
})
extended.value = extended.value
// Logs:
// custom getter
// custom setterSimilarly, an extendedRef-exclusive shallow boolean option may be set to true for an extendedRef's regular ref to be created with shallowRef.
const extended = extendedRef(undefined, null, { shallow: true })
console.log(isShallow(extended.ref)) // trueDifference with ref
Even though an extendedRef attempts to emulate a regular ref, they do not always behave in the same way since extendedRefs are not ref objects. Pragmatically, there are two major differences between extended and regular refs:
1. extendedRefs cannot be provided where refs are expected.
Consider, for instance, the watch function:
const extended = extendedRef()
watch(extended, callback) // extended is not a valid watch sourceThe extendedRef's ref property should be used instead:
watch(extended.ref, callback) // ok
watch(() => extended.value, callback) // getter also works2. extendedRefs are not unwrapped in SFC templates.
To access an extendedRef's inner ref value in SFC templates, full .value notation is typically necessary. This is a requirement in order for extended properties to be accessible in the template.
<template>
<p>value: {{ extended.value }}</p>
<button @click="extended.reset">reset</button>
</template>Nevertheless, extendedRefs implement a Symbol.toPrimitive method that simply unwraps their underlying refs. Thus, for situations where an extendedRef would be coerced to a primitive, .value notation might be omitted.
<template>
<p>value: {{ extended.value }}</p>
<p>value: {{ extended }}</p>
<button @click="extended.reset">reset</button>
</template>Definition
function extendedRef<
T extends MaybeRefOrGetter | ExtendedRef,
U = T extends ExtendedRef<infer R> ? UnwrapRef<R> : UnwrapRef<T>,
Extension extends Record<PropertyKey, unknown> | null = {}
>(
value: T,
extension?: Extension,
options?: ExtendedRefOptions<T extends ExtendedRef<infer R> ? UnwrapRef<R> : UnwrapRef<T>, U>
): T extends ExtendedRef<infer R, infer V, infer E>
? ExtendedRef<R, V, E & Extension>
: ExtendedRef<T, U, Extension>
interface ExtendedRefOptions<T = unknown, U = T> extends EntangledOptions {
shallow?: boolean;
get?: () => T;
set?: (value: U) => void;
}
type ExtendedRef<
T extends MaybeRefOrGetter = unknown,
U = UnwrapRef<T>,
Extension extends Record<PropertyKey, unknown> = {},
> = {
get value(): UnwrapRef<T>;
set value(v: U);
ref: ToRef<T>
} & Entangled<Omit<Extension, 'ref' | 'value'>>Parameters
value: Value to normalize into the ref to be extended.extension: Extension object whose keys represent the names or symbols of extendedRef properties to be defined, while its values represent those properties' initial values or descriptors (see property definition).options:shallow: Whether the created extendedRef's underlying ref is shallow. Defaults tofalse.get: Custom extendedRef'svaluegetter function.set: Custom extendedRef'svaluesetter function.defaults: Default value of theconfigurable,enumerable, andwritableoptions. Defaults totrue.configurable: Defaultconfigurableproperty value for descriptors of newly created properties. Defaults todefaults.enumerable: Defaultenumerableproperty value for descriptors of newly created properties. Defaults todefaults.writable: Defaultwritableproperty value for descriptors of newly created properties. Defaults todefaults.ignore: Array ofextensionproperty keys not to be defined on the underlying extendedRef object.
Return value
An extendedRef object.