When Nothing Changes: Understanding Computed and Non-Reactive Dependencies in Vue
A computed property only tracks reactive dependencies accessed during its execution, so any non-reactive values inside it are treated as constants, meaning changes to them will not trigger recomputation or updates.
Thereâs a moment where a computed property feels like it should workâ
but doesnât.
You write something simple:
const count = ref(1)
let multiplier = 2
const result = computed(() => {
return count.value * multiplier
})At first, everything behaves.
console.log(result.value) // 2Then later:
multiplier = 3
console.log(result.value)And nothing changes.
Still 2.
It feels wrong.
But it reveals something deeper about how Vue actually works.
Not Everything Inside Computed Is Reactive
Itâs easy to assume:
âIf itâs inside computed, Vue will track it.â
But Vue doesnât think that way.
It doesnât look at your code and decide what matters.
Instead, it follows a much stricter rule:
Only reactive access is tracked.
Not variables.
Not logic.
Not intention.
Just accessâthrough the reactivity system.
What Actually Gets Tracked
When the computed runs:
count.value * multiplierTwo things happen.
First:
count.valueThis goes through Vueâs system.
Itâs reactive.
It has a getter.
It gets tracked.
Then:
multiplierThis is just a plain variable.
No Proxy.
No interception.
No tracking.
So Vue records a dependency on countâ
and nothing else.
From Vueâs Perspective
The computed is not:
âcount times multiplierâ
It is:
âcount times something that never changesâ
Because as far as Vue can tellâ
multiplier is invisible.
When Changes Donât Exist
So when this happens:
multiplier = 3Vue doesnât react.
Not because itâs ignoring youâ
but because it never knew this value mattered.
There was nothing to track.
And without trackingâ
there is nothing to trigger.
A Computed Without Dependencies
Consider this:
const result = computed(() => Math.random())At first, it looks dynamic.
But it runs once.
And then never again.
Because nothing inside it is reactive.
No dependencies.
No invalidation.
Just a cached value.
Itâs no longer a computation.
Itâs a constant.
The Illusion of Reactivity
The tricky part is when you mix both worlds:
const result = computed(() => count.value * multiplier)Now it reactsâ
but only partially.
Changes in count trigger updates.
Changes in multiplier do not.
Which makes it feel like itâs workingâ
until it isnât.
Why Vue Works This Way
Vue doesnât:
- scan variables
- observe closures
- detect assignments
It relies entirely on one mechanism:
Intercepting access through reactive objects
If something doesnât go through that systemâ
it simply doesnât exist in Vueâs world.
Making It Visible
To make multiplier part of the system, it must become reactive:
const multiplier = ref(2)
const result = computed(() => {
return count.value * multiplier.value
})Now both values are tracked.
Now both can trigger updates.
Now the relationship is complete.
A Subtle Shift
At this point, it becomes clearer:
A computed property is not:
âA function that runs when something changesâ
It is:
A cached value derived from reactive inputs
And if an input is not reactiveâ
itâs treated as if it never changes at all.
A Final Thought
Reactivity doesnât come from where your code lives.
It comes from how your data is accessed.
You can write the most dynamic logic inside a computedâ
but if it doesnât touch reactive sources,
Vue sees nothing moving.
And in a system built on observationâ
what cannot be seen,
cannot change.