Provide / Inject
Esta página supone que ya has leído los Fundamentos de los Componentes. Léelo primero si eres nuevo en el tema de componentes.
Profundización de Prop
Normalmente, cuando necesitamos pasar datos del componente padre a un componente hijo, utilizamos props. Sin embargo, imaginemos el caso en el que tenemos un gran árbol de componentes, y un componente profundamente anidado necesita algo de un componente ancestral lejano. Con sólo props, tendríamos que pasar la misma prop a través de toda la cadena de padres:
Fíjate en que, aunque al componente <Footer>
no le importen en absoluto estas props, tiene que declararlas y pasarlas para que <DeepChild>
pueda acceder a ellas. Si hay una cadena de padres más larga, más componentes se verán afectados en el camino. Esto se llama "profundización de props" y definitivamente no es divertido de tratar.
Podemos resolver la profundización de props con provide
e inject
. Un componente padre puede servir como proveedor de dependencia para todos sus descendientes. Cualquier componente en el árbol descendiente, independientemente de su profundidad, puede inyectar dependencias proporcionadas por los componentes de su cadena padre.
Provide
Para proporcionar datos a los descendientes de un componente, utiliza la función provide()
:
vue
<script setup>
import { provide } from 'vue'
provide(/* key */ 'message', /* value */ 'hola!')
</script>
Si no usas <script setup>
, asegúrate de que provide()
sea sincrónicamente llamado dentro de setup()
:
js
import { provide } from 'vue'
export default {
setup() {
provide(/* key */ 'message', /* value */ 'hola!')
}
}
La función provide()
acepta dos argumentos. El primer argumento se llama key de inyección, que puede ser un string o un Symbol
. La key de inyección es utilizada por los componentes descendientes para buscar el valor deseado a inyectar. Un mismo componente puede llamar a provide()
varias veces con diferentes claves de inyección para proporcionar diferentes valores.
El segundo argumento es el valor proporcionado. El valor puede ser de cualquier tipo, incluyendo el estado reactivo como las refs:
js
import { ref, provide } from 'vue'
const count = ref(0)
provide('key', count)
Proporcionar valores reactivos permite a los componentes descendientes que utilizan el valor proporcionado establecer una conexión reactiva con el componente proveedor.
Nivel de Aplicación de Provide
Además de proporcionar datos en un componente, también podemos hacerlo a nivel de la aplicación:
js
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* key */ 'message', /* value */ 'hola!')
Los valores del nivel de la aplicación están disponibles para todos los componentes renderizados en la aplicación. Esto es especialmente útil cuando se escriben plugins, ya que los plugins normalmente no podrían proporcionar valores usando componentes.
Inject
Para inyectar datos proporcionados por un componente ancestro, utiliza la función inject()
:
vue
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
Si el valor proporcionado es una ref, se inyectará tal cual y no se desempaquetará automáticamente. Esto permite que el componente inyector conserve la conexión de reactividad con el componente proveedor.
Ejemplo de provide + inject Total con Reactividad
De nuevo, si no se utiliza <script setup>
, inject()
sólo debe llamarse de forma sincrónica dentro de setup()
:
js
import { inject } from 'vue'
export default {
setup() {
const message = inject('message')
return { message }
}
}
Valores Default de Injection
Por defecto, inject
supone que la key inyectada se proporciona en algún lugar de la cadena padre. En el caso de que no se proporcione la key, habrá una advertencia en tiempo de ejecución.
Si queremos que una propiedad inyectada funcione con proveedores opcionales, debemos declarar un valor por defecto, similar a las props:
js
// `value` será el "valor default"
// si no se proporcionan datos que coincidan con "message"
const value = inject('message', 'default value')
En algunos casos, puede ser necesario crear el valor por defecto llamando a una función o instanciando una nueva clase. Para evitar cálculos innecesarios o efectos secundarios en caso de que no se utilice el valor opcional, podemos utilizar una factory function para crear el valor por defecto:
js
const value = inject('key', () => new ExpensiveClass(), true)
El tercer parámetro indica que el valor por defecto debe tratarse como una función de fábrica.
Trabajando con Reactividad
Cuando se utilizan valores reactivos de provide / inject, se recomienda mantener las mutaciones del estado reactivo dentro del provider siempre que sea posible. Esto asegura que el estado proporcionado y sus posibles mutaciones están co-localizados en el mismo componente, haciendo más fácil su mantenimiento en el futuro.
Puede haber ocasiones en las que necesitemos actualizar los datos de un componente inyector. En estos casos, recomendamos proporcionar una función que se encargue de mutar el estado:
vue
<!-- dentro del componente proveedor -->
<script setup>
import { provide, ref } from 'vue'
const location = ref('Polo Norte')
function updateLocation() {
location.value = 'Polo Sur'
}
provide('location', {
location,
updateLocation
})
</script>
vue
<!-- en el componente inyector -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
<button @click="updateLocation">{{ location }}</button>
</template>
Finalmente, puedes envolver el valor proporcionado con readonly()
si quieres asegurarte de que los datos pasados a través de provide
no pueden ser mutados por el componente inyector.
vue
<script setup>
import { ref, provide, readonly } from 'vue'
const count = ref(0)
provide('read-only-count', readonly(count))
</script>
Trabajando con Symbol Keys
Hasta ahora, hemos estado utilizando key de inyección de cadenas en los ejemplos. Si estás trabajando en una aplicación grande con muchos proveedores de dependencias, o estás creando componentes que van a ser utilizados por otros desarrolladores, es mejor utilizar keys de inyección de Symbol para evitar potenciales conflictos.
Se recomienda exportar los Symbols en un archivo dedicado:
js
// keys.js
export const myInjectionKey = Symbol()
js
// en el componente proveedor
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'
provide(myInjectionKey, {
/* datos para proveer */
})
js
// en el componente inyector
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'
const injected = inject(myInjectionKey)
Véase también: Escritura de Provide / Inject