## Vue3 inject() Function
The `inject()` function is a core Composition API in Vue 3. It allows a child or deeply nested descendant component to retrieve dependencies provided by an ancestor component via the `provide()` function.
This mechanism is commonly referred to as **Dependency Injection**. It is designed to facilitate cross-level component communication, especially when dealing with deeply nested component trees where passing props down through every level (prop drilling) becomes impractical.
---
### Basic Syntax
```javascript
import { inject } from 'vue';
const value = inject(key, defaultValue, treatDefaultAsFactory);
```
#### Parameters:
* **`key`**: A string or a `Symbol` representing the unique identifier of the dependency you want to retrieve.
* **`defaultValue`** *(Optional)*: The fallback value returned if the specified key is not provided by any ancestor component.
* **`treatDefaultAsFactory`** *(Optional)*: A boolean indicating whether the `defaultValue` should be treated as a factory function. This is useful when the default value is expensive to compute or is a mutable object/array that should be instantiated per component instance.
---
## Common Use Cases
The `provide()` and `inject()` APIs are typically used together in the following scenarios:
1. **Deeply Nested Component Communication**: When components are nested several layers deep, using `provide()` and `inject()` avoids the tedious process of passing props through intermediate components that do not need them.
2. **Lightweight State Management**: For small-to-medium applications, `provide()` and `inject()` can serve as a lightweight alternative to dedicated state management libraries like Pinia or Vuex.
3. **Plugin and Library Development**: When developing Vue plugins or component libraries, `inject()` is a powerful tool to expose global configurations, services, or instances to consumer components.
---
## Code Examples
### 1. Basic Usage (Composition API with `
```
#### Descendant Component (Injecting Data)
The deeply nested child component uses `inject()` to resolve and consume the provided data.
```vue
Descendant Component
Received Message: {{ message }}
```
---
### 2. Advanced Usage: Using Factory Functions for Default Values
If the default value is a complex object or needs to be computed dynamically, you can pass a factory function as the second argument and set the third argument to `true`.
```javascript
import { inject } from 'vue';
// The factory function will only be executed if 'config' is not provided.
const config = inject('config', () => ({ port: 8080, host: 'localhost' }), true);
```
---
## Best Practices and Considerations
### 1. Maintaining Reactivity
If you pass a reactive reference (like a `ref` or `reactive` object) to `provide()`, the injected value in the child component remains fully reactive. Any changes made to the state in the parent component will automatically trigger updates in the child component.
### 2. Mutating Reactive Data (One-Way Data Flow)
To adhere to Vue's one-way data flow principle, it is recommended to **mutate reactive state only inside the component where it was defined**.
If a child component needs to update the injected data, you should provide a mutation function alongside the state:
```javascript
// Parent Component
const theme = ref('light');
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light';
};
provide('themeContext', {
theme,
toggleTheme // Pass the modifier function to the child
});
```
If you want to ensure that the injected data cannot be mutated by the child component, wrap the provided value with `readonly()`:
```javascript
import { provide, readonly, ref } from 'vue';
const count = ref(0);
provide('count', readonly(count)); // Child can read but cannot modify directly
```
### 3. Using Symbols for Keys
In large-scale applications or when writing reusable plugins, using string keys for `provide` and `inject` can lead to naming collisions. To prevent this, use a `Symbol` as the injection key:
```javascript
// keys.js
export const MyInjectionKey = Symbol('MyInjectionKey');
// Parent.vue
import { MyInjectionKey } from './keys.js';
provide(MyInjectionKey, 'secureData');
// Child.vue
import { MyInjectionKey } from './keys.js';
const data = inject(MyInjectionKey);
```
### 4. Always Provide a Default Value
If an injected key is not found in the ancestor chain, Vue will throw a runtime warning. To prevent this and make your components more resilient, always specify a sensible default value as the second argument to `inject()`.