--- title: Wrap Destructured Props in Getters for watch and Composables impact: MEDIUM impactDescription: Passing destructured prop values directly to watch or composables loses reactivity type: gotcha tags: [vue3, props, reactivity, watch, composables, destructuring] --- # Wrap Destructured Props in Getters for watch and Composables **Impact: MEDIUM** - When you destructure props with `defineProps`, the destructured variables are reactive in templates but passing them directly to `watch()` or external composables will pass the current value, not a reactive source. The watcher or composable won't track future changes. Vue 3.5+ automatically transforms destructured props for template reactivity, but external functions still need getter wrappers. ## Task Checklist - [ ] Wrap destructured props in arrow functions when passing to `watch()` - [ ] Use getter functions when passing destructured props to composables - [ ] Verify composables use `toValue()` to normalize getter/ref inputs - [ ] Consider using `props.propertyName` directly if getter syntax feels awkward **Incorrect:** ```vue ``` **Correct:** ```vue ``` ## Alternative: Use props Object Directly ```vue ``` ## Writing Composables That Accept Props When creating composables that should work with destructured props: ```javascript // composables/useDebounce.js import { ref, watch, toValue } from 'vue' export function useDebounce(source, delay = 300) { const debounced = ref(toValue(source)) // toValue handles both getter and ref let timeout watch( // toValue normalizes getter functions and refs () => toValue(source), (newValue) => { clearTimeout(timeout) timeout = setTimeout(() => { debounced.value = newValue }, delay) } ) return debounced } ``` ```vue ``` ## Vue 3.5+ Reactive Destructuring Vue 3.5+ added reactive props destructuring. The compiler transforms: ```javascript const { foo } = defineProps(['foo']) ``` Into something like: ```javascript const __props = defineProps(['foo']) // foo accesses __props.foo reactively in templates ``` However, external function calls still need getters because JavaScript itself passes values, not references. ## Reference - [Vue.js Props - Reactive Props Destructure](https://vuejs.org/guide/components/props.html#reactive-props-destructure) - [Vue.js Watchers - Watch Source Types](https://vuejs.org/guide/essentials/watchers.html#watch-source-types)