# Refs

Esta sección utiliza la sintaxis de componentes de un solo archivo para ejemplos de código

# ref

Toma un valor interno y retorna un objeto reactivo y mutable de ref. El objeto de ref tiene una sola propiedad .value que apunta al valore interno.

Ejemplo:

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1
1
2
3
4
5

Si un objeto es asignado como el valor de una ref, el objeto se hace reactivo profundamente por la función reactive.

Tipar:

interface Ref<T> {
  value: T
}

function ref<T>(value: T): Ref<T>
1
2
3
4
5

Algunas veces necesitaríamos especificar tipos complejos para un valor interno de una ref. Podemos hacerlo sucintamente mediante pasar un argumento genérico cuando llame ref para sobreescribir la inferencia por defecto:

const foo = ref<string | number>('foo') // el tipo de foo: Ref<string | number>

foo.value = 123 // ok!
1
2
3

Si el tipo del genérico es desconocido, es recomendable fundir ref a Ref<T>:

function useState<State extends string>(initial: State) {
  const state = ref(initial) as Ref<State> // state.value -> State extiende string
  return state
}
1
2
3
4

# unref

Retorna el valor interno si el argumento es una ref, de lo contrario retorna el argumento mísmo. Este es una función súgar para val = isRef(val) ? val.value : val.

function useFoo(x: number | Ref<number>) {
  const unwrapped = unref(x) // unwrapped está garantizado para ser un número ahora
}
1
2
3

# toRef

Puede ser utilizado para crear una ref para una propiedad en un objeto reactivo original. La ref puede luego ser pasada libremente, reteniendo la conexión a su propiedad original.

const state = reactive({
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3
1
2
3
4
5
6
7
8
9
10
11
12

toRef es útil cuando quiere pasar la ref de una prop a una función de composición:

export default {
  setup(props) {
    useSomeFeature(toRef(props, 'foo'))
  }
}
1
2
3
4
5

toRef retornará una ref utilizable incluso si la propiedad original no existe en la actualidad. Este lo hace específicamente útil cuando se trabaja con props opcionales, las que no serían recogidas por toRefs.

# toRefs

Covierte un objeto reactivo a un objeto plano dónde cada propiedad del objeto resultante es una ref que apunte a la propiedad correspondiente del objeto original.

const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
/*
Tipo de stateAsRefs:

{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// La ref y la propiedad original son "vinculadas"
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

toRefs es útil cuando retorna un objeto reactivo desde una función de composición para que el componente que consume las refs pueden desestructurar/extender el objeto retornado sin perder la reactividad:

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })

  // operación lógica en _state_

  // convertirlo a refs al tiempo de retornarlo
  return toRefs(state)
}

export default {
  setup() {
    // puede desestructurarse sin perder reactividad
    const { foo, bar } = useFeatureX()

    return {
      foo,
      bar
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

toRefs solo generará refs para propiedades que son incluidas en el objeto original. Para crear una ref para una propiedad específica, utilice toRef en su lugar.

# isRef

Comprueba si un valor es un objeto de ref.

# customRef

Crea una ref personalizada con control explícito sobre su seguimiento de dependencias y el disparar de actualizaciones. Espera una función de factoría, la que recibe funciones track y trigger como argumentos y debe retornar un objeto con get y set.

  • Ejemplo que utiliza una ref personalizada para implementar debounce con v-model:

    <input v-model="text" />
    
    1
    function useDebouncedRef(value, delay = 200) {
      let timeout
      return customRef((track, trigger) => {
        return {
          get() {
            track()
            return value
          },
          set(newValue) {
            clearTimeout(timeout)
            timeout = setTimeout(() => {
              value = newValue
              trigger()
            }, delay)
          }
        }
      })
    }
    
    export default {
      setup() {
        return {
          text: useDebouncedRef('hello')
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

Tipar:

function customRef<T>(factory: CustomRefFactory<T>): Ref<T>

type CustomRefFactory<T> = (
  track: () => void,
  trigger: () => void
) => {
  get: () => T
  set: (value: T) => void
}
1
2
3
4
5
6
7
8
9

# shallowRef

Crea una ref que rastrea su propia mutación de .value pero no lo hace reactivo a su valor.

const foo = shallowRef({})
// mutar el valor de la ref es reactivo
foo.value = {}
// pero el valor no será convertido.
isReactive(foo.value) // false
1
2
3
4
5

Vea también: crear valores reactivos independientes como refs

# triggerRef

Ejecuta cuaqlquieres efectos vinculados a shallowRef manualmente.

const shallow = shallowRef({
  greet: 'Hola, mundo'
})

// registra "Hola, mundo" una vez para la primera ejecución
watchEffect(() => {
  console.log(shallow.value.greet)
})

// Este no disparará el efecto porque la ref es _shallow_
shallow.value.greet = 'Hola, universo'

// registra "Hola, universo"
triggerRef(shallow)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Vea también: computed y watch - watchEffect