Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/provide-inject-default-value-factory.md
1---2title: Use Factory Functions for Non-Primitive Inject Default Values3impact: MEDIUM4impactDescription: Using object literals as default values creates shared references across all consuming components5type: gotcha6tags: [vue3, provide-inject, composition-api, memory, shared-state]7---89# Use Factory Functions for Non-Primitive Inject Default Values1011**Impact: MEDIUM** - When providing default values for `inject()`, using an object literal creates a single shared reference. All components using that default will share the same object, leading to unexpected state sharing and bugs.1213## Task Checklist1415- [ ] Always use factory functions for object/array default values in inject16- [ ] Pass `true` as the third argument to enable factory mode in Composition API17- [ ] Use the object syntax with factory function in Options API18- [ ] Only use literal defaults for primitive values (strings, numbers, booleans)1920## The Gotcha: Shared Default References2122**Wrong - Object literal creates shared reference:**23```vue24<script setup>25import { inject } from 'vue'2627// WRONG: All components without a provider share this SAME object28const config = inject('config', { debug: false, apiUrl: '' })2930// If one component does this:31config.debug = true3233// ALL other components using this default now have debug: true!34</script>35```3637**Correct - Factory function creates unique instance:**38```vue39<script setup>40import { inject } from 'vue'4142// CORRECT: Each component gets its own object43// Third argument `true` indicates the second arg is a factory function44const config = inject('config', () => ({ debug: false, apiUrl: '' }), true)45</script>46```4748## API Explanation4950The `inject()` function has multiple signatures:5152```ts53// Simple default value (OK for primitives)54inject(key, defaultValue)5556// Factory function for non-primitives (REQUIRED for objects/arrays)57inject(key, factoryFunction, true)58```5960The third argument `true` tells Vue that the second argument is a factory function, not the default value itself.6162## Examples6364### Primitive Defaults (No Factory Needed)6566```vue67<script setup>68import { inject } from 'vue'6970// Primitives are safe without factory71const count = inject('count', 0)72const name = inject('name', 'Guest')73const enabled = inject('enabled', false)74</script>75```7677### Object Defaults (Factory Required)7879```vue80<script setup>81import { inject } from 'vue'8283// Objects MUST use factory84const user = inject('user', () => ({85id: null,86name: 'Anonymous',87preferences: {}88}), true)8990const settings = inject('settings', () => ({91theme: 'light',92language: 'en',93notifications: true94}), true)95</script>96```9798### Array Defaults (Factory Required)99100```vue101<script setup>102import { inject } from 'vue'103104// Arrays MUST use factory105const items = inject('items', () => [], true)106const permissions = inject('permissions', () => ['read'], true)107</script>108```109110### Class Instance Defaults (Factory Required)111112```vue113<script setup>114import { inject } from 'vue'115import { Logger } from '@/utils/logger'116117// Class instances MUST use factory118const logger = inject('logger', () => new Logger({ level: 'warn' }), true)119</script>120```121122## Options API Syntax123124In Options API, use the object syntax with a `default` factory function:125126```js127export default {128inject: {129// Primitive - can use literal130theme: {131from: 'theme',132default: 'light'133},134135// Object - MUST use factory136config: {137from: 'config',138default: () => ({ debug: false })139},140141// Array - MUST use factory142permissions: {143from: 'permissions',144default: () => []145}146}147}148```149150## Real-World Example: Form Context151152```vue153<!-- FormProvider.vue -->154<script setup>155import { provide, reactive } from 'vue'156157const formContext = reactive({158values: {},159errors: {},160touched: {},161isSubmitting: false162})163164provide('formContext', formContext)165</script>166167<!-- FormField.vue (might be used outside FormProvider) -->168<script setup>169import { inject } from 'vue'170171// Safe default that won't be shared172const formContext = inject('formContext', () => ({173values: {},174errors: {},175touched: {},176isSubmitting: false,177// Mark as standalone mode178isStandalone: true179}), true)180181// Component works both inside and outside FormProvider182</script>183```184185## TypeScript: Typing Factory Defaults186187```ts188import { inject } from 'vue'189import type { InjectionKey } from 'vue'190191interface Config {192apiUrl: string193debug: boolean194features: string[]195}196197const ConfigKey: InjectionKey<Config> = Symbol('config')198199// TypeScript understands the factory return type200const config = inject(ConfigKey, () => ({201apiUrl: 'https://api.example.com',202debug: false,203features: []204}), true)205```206207## Common Mistake in Testing208209This gotcha often appears in tests where components are rendered without providers:210211```ts212// test.spec.ts213import { mount } from '@vue/test-utils'214import MyComponent from './MyComponent.vue'215216// Without provider, all test instances share the wrong default217it('test 1', () => {218const wrapper = mount(MyComponent)219wrapper.vm.config.debug = true // Pollutes other tests!220})221222it('test 2', () => {223const wrapper = mount(MyComponent)224// Might fail because debug is still true from test 1225})226```227228**Fix: Use factory functions in the component, or provide in tests:**229230```ts231it('test with provider', () => {232const wrapper = mount(MyComponent, {233global: {234provide: {235config: { debug: false, apiUrl: '' }236}237}238})239})240```241242## Reference243- [Vue.js inject() API Reference](https://vuejs.org/api/composition-api-dependency-injection.html#inject)244- [Vue.js Provide/Inject Guide](https://vuejs.org/guide/components/provide-inject.html)245