Vue 3: Stop Using ref
and watch
for Props – Use computed
Instead!
If you’re working with Vue 3 and often create local copies of props using ref()
, then manually sync changes with watch()
and emit updates, you might be doing more work than necessary. There's a cleaner way: computed properties!
The Common Anti-Pattern: ref
+ watch
Since Vue enforces one-way data flow, props should never be changed inside a component. However, many developers try to work around this by:
- Creating a local copy of the prop with
ref([...props.someProp])
. - Watching the prop to update the local value when the parent changes.
- Watching the local value to emit an update back to the parent.
This results in unnecessary complexity like this:
const localValue = ref([...props.someProp]); // Local copy of prop
// Sync local value when prop changes
watch(() => props.someProp, (newVal) => {
localValue.value = [...newVal];
});
// Emit updates when local value changes
watch(localValue, (newVal) => {
emit('update:someProp', newVal);
});
Why is this a bad pattern?
❌ Unnecessarily complex — Using two watch()
calls for something Vue can handle automatically.
❌ Harder to maintain – More moving parts, more potential for bugs.
❌ Inefficient – Each change triggers a new update cycle, making reactivity management more complicated than necessary.
The Vue 3 Way: computed
Properties
Instead of this workaround, Vue’s computed properties provide a built-in way to:
✔ Keep the local value in sync with the parent prop.
✔ Automatically update the parent when changed.
✔ Reduce code complexity.
Here’s the better way using computed
:
const localValue = computed({
get: () => props.someProp,
set: (newVal) => emit('update:someProp', newVal),
});
This does everything the ref + watch
approach did—but in just a few lines!
Final Thoughts
Next time you think about making a local copy of a prop and manually syncing it, pause and try using computed
instead. Your code will be shorter, cleaner, and more maintainable! 🚀