Search code examples
vue.jsvuejs2vuejs3vue-component

Refs inside arrays unexpected behavior


I am trying to use an v-model with a custom component. More specifically I was using an v-for to generate a list of elements based on infomations in an array. In practice, the components did not behave as I expected... the problem seems to arise from how the ref(s) are treated and I can't find the reason for it nor a solution.

This is my test code, from which I expect the same result on both rows, but it doesn't:

// App.vue
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
  
const message = ref('hello')
const myArr = [message]
 
</script>

<template>
  <CustomInput v-model="message" /> {{ message }}
  <br/>
  <CustomInput v-model="myArr[0]" /> {{ myArr[0] }}
</template>
// CustomInput.vue
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>

Playground Project

I premise that I am new to Vue, and have only read a small part of the documentation at the moment, so I apologize if there is anything I missed that would allow a simple and obvious solution to the question.

I tried wrapping the array in a ref or reactive (read around) but it doesn't seem to work.

I am sure there are solutions such as hardcoding the components to solve my original problem. But mine is mainly an educational question, so the code shown above I think extrapolates well to the problem I'm interested in. Thank you in advance for your answers.


Solution

  • Normally a ref used in your <template> code is automatically "unwrapped", so v-model="message" actually becomes v-model="message.value", where .value is the inner value 'hello'.

    With v-model="myArr[0]" you're basically hiding the fact that it's a ref by assigning an array index. Vue isn't that smart to know it's a ref in this case, which results in the entire ref proxy object being assigned to the v-model. This is why you see [object Object] in the input instead of the inner value. The simple fix is to unwrap the value yourself by specifying .value

    <CustomInput v-model="myArr[0].value" /> {{ myArr[0].value }}
    

    updated Playground