skip to Main Content

I have several similar elements that are defined as follows:

<script setup>
const el1 = ref()
const el2 = ref()
const el3 = ref()
</script>

<template>
<h1 ref="el1"></h1>
<h1 ref="el2"></h1>
<h1 ref="el3"></h1>
</template>

I want to combine all of ref elements into a new reactive variable and use their attributes.

What is the best approach for handling this?

3

Answers


  1. For looping over multiple items, use refs inside v-for.

    For joining multiple separate refs, use reactive():

    • in <script>
    <script>
    import { reactive, toRefs } from 'vue'
    
    export default defineComponent({
      setup() {
        const state = reactive({
          el1: null,
          el2: null,
          el3: null
        })
        return toRefs(state)
        // or return { ...toRefs(state), /* moreStuff */ }
    
      }
    })
    </script>
    
    • in <script setup>

    You have 2 options:

    1. do not extract (use them as state.el1, state.el2 in <template />)
    <script setup>
    const state = reactive({
      el1: null,
      el2: null,
      el3: null
    })
    onMounted(() => {
      console.log(state.el1)
    })
    </script>
    <template>
      <h1 ref="state.el1"></h1>
      <h1 ref="state.el2"></h1>
      <h1 ref="state.el3"></h1>
    </template>
    
    1. extract them (using toRefs) – Con: you have to specify each ref when extracting:
    <script setup>
    import { reactive, toRefs, onMounted } from 'vue'
    const state = reactive({
      el1: null,
      el2: null,
      el3: null
    })
    const { el1, el2, el3 } = toRefs(state)
    onMounted(() => {
      console.log(el1.value)
      console.log(state.el2)
    })
    </script>
    

    The bigger advantage of using reactive is that you no longer need to use .value inside the script (Use state.el1 in both <script> and <template>.

    Login or Signup to reply.
  2. Try the following and let me know if that’s what you were trying to achieve 🙂

    <template>
      <div>
        <h1 ref="el1">Element 1</h1>
        <h1 ref="el2">Element 2</h1>
        <h1 ref="el3">Element 3</h1>
      </div>
    </template>
    
    <script setup>
    import { reactive, toRefs, ref } from 'vue';
    
    const state = reactive({
      el1: ref(),
      el2: ref(),
      el3: ref(),
    });
    
    const { el1, el2, el3 } = toRefs(state);
    
    console.log(el1, el2, el3);
    // ObjectRefImpl {_object: Proxy(Object), _key: 'el1', _defaultValue: undefined, __v_isRef: true}
    // ObjectRefImpl {_object: Proxy(Object), _key: 'el2', _defaultValue: undefined, __v_isRef: true}
    // ObjectRefImpl {_object: Proxy(Object), _key: 'el3', _defaultValue: undefined, __v_isRef: true}
    </script>
    

    edit: if you want to loop dynamically, try the following

    <template>
      <h1 v-for="el in refsArr" ref="el">{{ el._key }}</h1>
    </template>
    
    <script setup>
    import { reactive, toRefs, ref } from 'vue';
    
    const state = reactive({
      el1: ref(),
      el2: ref(),
      el3: ref(),
    });
    
    const refsObj = toRefs(state);
    
    const refsArr = []
    for (const key in refsObj) {
      refsArr.push(refsObj[key]);
    }
    </script>
    
    Login or Signup to reply.
  3. Shown in the example below two cases, in v-for and through a function :ref(if you need to combine elements without a loop):

    Vue SFC Playground

    Docs: Refs inside v-for and Function Refs.

    <script setup>
    import { ref, onMounted } from 'vue';
      
    const list = [1, 2, 3];
    const listRefs = ref([]);
    
    const titlesRefs = ref([]);
    
    onMounted(() => {
      console.log(listRefs.value);
      console.log(titlesRefs.value);
    })
    
    </script>
    
    <template>
      <h1 v-for="item in list" ref="listRefs">{{ item }}</h1>
      <hr>
      <h1 :ref="el => titlesRefs.push(el)">1</h1>
      <h2 :ref="el => titlesRefs.push(el)">2</h2>
      <h3 :ref="el => titlesRefs.push(el)">3</h3>
    </template>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search