Let me start by saying: sure React is great, Angular is enterprise-ready, but my love falls on Vue.
The reactivity system? Chef’s kiss.
Watching values magically update the DOM like it’s reading your mind? Ammmazing.
But sometimes, it feels less like magic and more like an unfair duel!
You change a value, and five things re-render. ☠️
You watch a computed property… and it triggers in places it has no business being in. ☠️
You swear you only updated one thing, and now your whole app is unresponsive. ☠️
Let’s talk about when Vue’s reactivity system goes rogue, and how to stop it.
You add a watcher to a prop or some reactive state, thinking:
“This’ll run when the user changes something. Simple.”
Then it runs on mount. ☠️
Then it runs when nothing changed. ☠️
Then it runs in a loop. ☠️
For example:
watch(() => props.value, (newVal) => {
doSomething(newVal)
})
If props.value
is an object or array and you mutate it in place (hello, non-immutable operations), this thing will fire again and again, even if the actual content didn’t change!
Use deep:true
only when you absolutely need to. And make sure you really do need to, it’s very easy to misuse it.
Also, consider watchEffect
only for setup-y stuff, and avoid mutating inside it.
This is way more common that you think. For instance:
const result = computed(() => {
doSomething()
return data.value + 1
})
Please don’t. Pretty please. Really.
Computed properties are not side-effect land. They’re for deriving state, not triggering logic.
If you need to “do stuff” use a watch
or watchEffect
.
Otherwise, Vue will try to cache and optimize your computed
, and you’ll end up with spooky bugs like:
Vue 3 gave us ref()
and reactive()
and said “go forth and be free.” And then half the dev community forgot which one does what (me included).
A quick cheat sheet:
You Want | Use | Notes |
---|---|---|
A single value (string, number, boolean, etc.) | ref() | Use .value to access |
An object or array | reactive() | Proxy magic, no .value |
To use it with v-model | Usually ref() | Simpler and reactive |
Misusing these will lead to situations like:
const form = ref({
name: '',
email: ''
})
And then someone inevitably does:
form.name = 'oh sh-'
That’s not how ref()
works with objects.
You’re supposed to do: form.value.name = 'correct'
Or, better yet:
Just use reactive()
for object-like state and save your sanity.
DISCLAIMER:
In the spirit of full transparency, I admit that more often than not I stick to ref()
, mostly because I’ve gotten super used to it by now, and I then have a consistent way to access data at all times. But if you’re new to Vue3, I’d advise sticking to reactive()
for objects and arrays.
This is the big one. Ooooh boy, how many times I’ve seen people driven nuts by this nasty little one.
Setting: you have a watcher or a computed that modifies something that it’s also watching.
Classic example:
watch(() => someValue.value, (val) => {
someValue.value = transform(val)
})
Congrats. You’ve invented recursion.
Vue will try to warn you, but sometimes the loop is subtle. Like when a watchEffect
modifies something that indirectly triggers the same watcher again.
Pro tip:
Watch component state, see what’s reactive, and trace updates like a normal human being with 2 thumbs.
Stick console.log
in your watchers and computed functions to catch unexpected triggers. I know, I know, here’s where people will yell in outrage “use the debugger!”.
Quite honestly though, console.log is way more effective while developing.
Simply put, if you see 2 identical console log lines for each action, something is most likely wrong. Just leave them there (IN DEV) as a means to make sure no side effects happen while you add functionalities.
Keep in mind that most times your integration or playwright tests will not fail even if you’re unnecessarily re-rendering pages and components. But your app will work like crap and the UX will take a huge hit.
onBeforeUnmount
cleanupYou might think your watcher is gone, but it’s still hanging out in the shadows. Clean it up. Also clean up $on(...)
listeners, jeez.
Vue’s reactivity is powerful and elegant, and it occasionally backfires. As for all things, we must be careful when employing it.
The key is to treat it like a smart-but-sensitive co-worker. Give it clear instructions. Don’t overload it with side-effects. And don’t assume it knows what you meant.
Because when reactivity goes rogue, it doesn’t break loudly.
It just slowly turns your app into a ghost town of unpredictable bugs.
And trust me on this, it will drive you crazy. Especially if more people work on the same codebase at the same time! Oh God, the PTSD 😱
So next time something updates when it shouldn’t, or doesn’t update when it should, look under the hood.
Remember (I repeat this to myself this more than I like to admit):
How you do anything is how you do everything
Don’t rush things just because it seems unimportant, or you’re very sure it won’t create any problems.
Every brick needs to be put down properly, as it shares the weight of the whole house just as much as any other one!
Big thanks for reading!
Did you find this article interesting? Does it match your skill set? Programming is at the heart of how we develop customized solutions. In fact, we’re currently hiring for roles just like this and others here at Würth Phoenix.