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.value
The 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 setter
Similarly, 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)) // true
Difference 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 source
The extendedRef's ref
property should be used instead:
watch(extended.ref, callback) // ok
watch(() => extended.value, callback) // getter also works
2. 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'svalue
getter function.set
: Custom extendedRef'svalue
setter function.defaults
: Default value of theconfigurable
,enumerable
, andwritable
options. Defaults totrue
.configurable
: Defaultconfigurable
property value for descriptors of newly created properties. Defaults todefaults
.enumerable
: Defaultenumerable
property value for descriptors of newly created properties. Defaults todefaults
.writable
: Defaultwritable
property value for descriptors of newly created properties. Defaults todefaults
.ignore
: Array ofextension
property keys not to be defined on the underlying extendedRef object.
Return value
An extendedRef object.