ref in v-for does not trigger watchEffect inside defineComponent
Vue version
3.2.37
Link to minimal reproduction
Steps to reproduce
Notice that this code is within defineComponent
and within a setup()
function
What is expected?
Exact same code, but within a <script setup>
and obviously doesn't 'return' the refs.
What is actually happening?
When you move the range handle, you can see that is correctly adds/removes divs to the DOM
Within <script setup>
the watchEffect triggers for each adding/removal of dom elements to inputRef
as the range slider is triggered,
but within defineComponent()
there is an initial triggering, but as you move the range slider, it never triggers again on updates.
System Info
System:
OS: Windows 10 10.0.19044
CPU: (8) x64 Intel(R) Xeon(R) CPU E5-1620 v3 @ 3.50GHz
Memory: 6.94 GB / 15.92 GB
Binaries:
Node: 16.6.0 - C:\Program Files\nodejs\node.EXE
npm: 7.19.1 - C:\Program Files\nodejs\npm.CMD
Browsers:
Chrome: 102.0.5005.115
Edge: Spartan (44.19041.1266.0), Chromium (102.0.1245.44)
Internet Explorer: 11.0.19041.1566
Any additional comments?
All I really wanted to do was ensure ref
for v-for
was really working as expected. I didn't know this big hoohar would happen.
Also things to note.
If you pre-populate inputRef
with an array, this array is thrown away and replaced with another array.. but it will continue to use that array thereafter.
Not really a problem for me. Just unexpected behaviour.
Here's why this is happening:
in <script setup>
:
- the generated render function can pass the ref itself to the vnode, and
- when the ref's value is set, we read the current value in the process, thereby registering it as a reactive dependency.
in normal setup()
:
- the generated rener function passes the ref's name as a string to the vnode, and
- when setting the ref, it's set via
instance.setupState
, but we never read its value from there - it's read frominstance.refs
, which is not reactive. So it's not registered as a render dependency.
First possible solution coming to mind: Read the refs previous value from instance.setupState
if present, instance.refs
otherwise.
const existing = _isString ? hasOwn(setupState, ref) : setupState[ref] ? refs[ref] : ref.value
Here's why this is happening:
in
<script setup>
:
- the generated render function can pass the ref itself to the vnode, and
- when the ref's value is set, we read the current value in the process, thereby registering it as a reactive dependency.
in normal
setup()
:
- the generated rener function passes the ref's name as a string to the vnode, and
- when setting the ref, it's set via
instance.setupState
, but we never read its value from there - it's read frominstance.refs
, which is not reactive. So it's not registered as a render dependency.First possible solution coming to mind: Read the refs previous value from
instance.setupState
if present,instance.refs
otherwise.const existing = _isString ? hasOwn(setupState, ref) : setupState[ref] ? refs[ref] : ref.value
Great👍, it can be fixed.
const existing = _isString ? hasOwn(setupState, ref) ? setupState[ref] : refs[ref] : ref.value