Issue with Vue 3: Composition API does not support Array of refs

Check out the code snippet below.

<template>
  <div v-for="item in arr" :key="item">{{ item }}</div>
</template>

<script>
import { ref } from "vue";

export default {
  name: "TestArr",
  setup() {
    const arr = [];
    arr.push(ref("a"));
    arr.push(ref("b"));
    arr.push(ref("c"));
    return { arr };
  }
};
</script>

Here is the output:

{ "_rawValue": "a", "_shallow": false, "__v_isRef": true, "_value": "a" }
{ "_rawValue": "b", "_shallow": false, "__v_isRef": true, "_value": "b" }
{ "_rawValue": "c", "_shallow": false, "__v_isRef": true, "_value": "c" }

Expected output:

a
b
c

In order to make it work, I need to call item.value in the template. Is there a workaround for this situation in vue3?

Cheers!

Answer №1

Incorrect approach; consider following this revised method

setup() {
    const array = ref([]);
    array.value.push("a");
    array.value.push("b");
    array.value.push("c");
    return { array };
  }

Avoid adding ref items to a regular array. The Array itself should be ref.

Answer №2

Insights on Using Arrays with ref() and reactive()

As I delved into learning the composition API while developing a basic todo list application, I encountered challenges related to handling arrays using ref() and reactive(). Through this experience, I gained insights that could be beneficial for others exploring the composition API. Hence, I decided to jot down my thoughts here. Feel free to provide feedback if you spot any errors!

1. Challenges Encountered When Using reactive() with Arrays

Initially, everything seemed to work seamlessly until I started working on the delete function.

I attempted to create a button that would invoke the deleteHandler function upon being clicked. This function was supposed to filter out elements from the todos array:

This is an excerpt of my code:

<template>
    <div>
        <h1>Using reactive</h1>
        <button @click="add">Click</button>
        <div v-for="item in todos" :key="item">
            <button @click="mark(item)">Mark</button>
            <span>{{item}}</span>
            <button @click="deleteHandler(item.id)">Delete</button>
        </div>
    </div>
</template>
<script>
import { reactive, ref } from "vue";

export default {    
    name: "ReactiveMethod",
    setup(){
        let todos = reactive([])
        const id = ref(0);
        function add(){
            todos.push({id:id.value, name:"hallo", state:"undone"});
            id.value += 1
        }
        function mark(item){
            if(item.state === "undone"){
                item.state = "done";
            }else{
                item.state = "undone";
            }
        }
        function deleteHandler(id){
            const temp = todos.filter((element) => {
                return element.id !== id
            });
            todos = temp;  
        }
        return {
            todos,
            id,
            deleteHandler,
            add,
            mark
        };
    }
}
</script>

However, a crucial issue arose as the filter function did not mutate the original value but instead returned a new value. Consequently, Vue failed to detect changes within the todos array.

To address this challenge, I made a modification to my code. Instead of assigning todos as reactive([]), I encapsulated the array within an object like so -> reactive({ todos: [] }). This adjustment resolved the problem!

<template>
    <div>
        <h1>Using reactive</h1>
        <button @click="add">Click</button>
        <div v-for="item in todos" :key="item">
            <button @click="mark(item)">Mark</button>
            <span>{{item}}</span>
            <button @click="deleteHandler(item.id)">Delete</button>
        </div>
    </div>
</template>
<script>
import { reactive, ref, toRefs } from "vue";

export default {    
    name: "ReactiveMethod",
    setup(){
        const state = reactive({
            todos: []
        });
        const id = ref(0);
        
        function add(){
            state.todos.push({ id: id.value, name: "hallo", state: "undone" });
            id.value += 1;
        }
        
        function mark(item){
            if(item.state === "undone"){
                item.state = "done";
            } else {
                item.state = "undone";
            }
        }
        
        function deleteHandler(id){
            const temp = state.todos.filter((element) => {
                return element.id !== id;
            });
            
            state.todos = temp;  
        }
        
        return {
            ...toRefs(state),
            id,
            deleteHandler,
            add,
            mark
        };
    }
}
</script>

Conclusion

It appears that Vue can only observe changes with the same reference (objects in JavaScript are called by reference), and cannot detect changes when the reference itself is altered. Thus, I believe that "wrapping the array inside an object" presents a more effective approach to dealing with arrays in the composition API.

2. Usage of ref() for Primitive and Reactive Values

Based on prevalent information, the general consensus seems to advocate for:

ref() for primitive values and reactive() for object values

Nevertheless, even if we write code in the following manner, Vue is still capable of detecting changes within it:

const obj = ref({ name: "charles" });

return {
    ...toRefs(obj)
}

The rationale behind this lies in the fact that when data is passed into ref(), it first verifies whether the data is primitive or an object. If it's an object, ref() calls upon reactive() to handle it. In essence, reactive() is the one actually undertaking the task behind the scenes.

Final Thoughts

At present, it seems feasible to use ref() across various scenarios. Nonetheless, I opine that it's preferable to utilize reactive() for objects and ref() for primitives to maintain clear distinctions! (If you have any insights regarding this matter, do share them with me!)

Answer №3

The correct answer is as follows:

setup() {
    const array = ref([]);
    array.value.push("a");
    array.value.push("b");
    array.value.push("c");
    console.log(array.value)
    return { array };
  }

While this option may work, the first one provided is ultimately superior.

const reactiveArray = reactive([]);
      reactiveArray.push("a")
      reactiveArray.push("b")
      reactiveArray.push("c")
      console.log(reactiveArray)

Answer №4

One way to access them is by using the value field :

  setup() {
    const arr = [];
    arr.push(ref("a").value);
    arr.push(ref("b").value);
    arr.push(ref("c").value);
    return { arr };
  }

However, it is considered a bad practice. A better approach would be to define your array as a ref and then push values to it:

  setup() {
    const arr = ref([]);
    arr.value.push("a");
    arr.value.push("b");
    arr.value.push("c");
    return { arr };
  }

Another solution is to initialize the array with specific values:

  setup() {
    const arr = ref(["a", "b", "c"]);
   
    return { arr };
  }

Answer №5

Here is an alternative way to accomplish the task:

setup() {
  const newArr = ref([]);
  newArr.value.push("x");
  newArr.value.push("y");
  newArr.value.push("z");
  return { newArr };
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Display an aspx page within a div container

I am using the following code to load an aspx page in a div tag. Unfortunately, it's not working as expected. Can someone please assist me in resolving this issue? <script type="text/javascript"> $(document).ready(function () { $(&a ...

Steps to create a typewriter effect using a collection of words

I managed to create a looping typewriter effect, but I'm facing an issue where the first word in the array is not being typed again after the first cycle. Additionally, I am seeing 'undefined' displayed after the last word before it repeats ...

Issue with dynamic form JavaScript functionality after removing curly braces { } from a select tag in Rails

In my Rails form, there is a gender field defined as follows: <%= f.select :gender, ["Male","Female"],{class: "gender"} %> I also tried adding an onclick event like this: <%= f.select :gender, ["Male","Female"],{class: "gender"},onclick: "categ ...

Pull the data from jQuery/JavaScript/AJAX and store it in the database using ASP.NET/C#

I am working on creating a form that includes textboxes and a five star rating feature. The goal is to save the data entered in the fields into a database upon submitting. While implementing the textboxes was straightforward, I am facing challenges with e ...

What is the best way to change the status of a disabled bootstrap toggle switch?

I'm working with a read-only bootstrap toggle that is meant to show the current state of a system (either enabled or disabled). The goal is for it to update every time the getCall() function is called. However, even though the console logs the correct ...

What causes the discrepancy in the output of `document.documentElement.childNodes` in JavaScript?

While working on my code exercise today, I came across a special case regarding the "document.documentElement.childNodes" property. Originally, I believed it to represent all child nodes within a tag as before. However, when implementing my code, I noticed ...

Adding JavaScript to dynamically loaded AJAX content

I'm new to AJAX and JavaScript and unsure of how to get it working. Here is the website: When you click on portfolio images, the details load via AJAX. I want to create a slideshow for projects with multiple full-sized images. However, due to the co ...

Ensuring User Information Persistence in React Upon Successful Login

I am developing a small application using React with PHP as the back-end. Within my database, I have two types of users - admin and student. Upon user login, I store their information in session storage like this ( user: { username:'abcxyz123', r ...

Problem with BeforeRouteEnter causing failure to populate data in variable

After setting up a web API that successfully sends data, I encountered an issue with my Vuejs app's data table component. Even though I made an API call using Axios within the BeforeRouteEnter hook, the data from the response doesn't seem to save ...

Export web application content to PDF through rendering on the server side

Our interactive web application, which includes multiple d3 charts, is built with vue. Currently, I am able to export our webpage to a PDF file by utilizing canvg and html2canvas to convert the content into PNG format. The PNG file is then transmitted to t ...

Transform pixel padding into percentage ratios

I've been searching through various discussions to find a solution, but none of them seem to fit my particular scenario. My issue involves a hyperlink with absolute positioning inside a div with relative positioning. The padding and margins are curre ...

Nextjs Version 13: Implementing a Loading UI for Search Parameter Changes

I am working on a component that handles user input and updates search parameters accordingly. This results in a page refresh to display updated data on the UI. However, despite these actions, the loading.tsx file for this route is not being triggered. Af ...

Having trouble locating the data-testid attribute within a Vue Component using Jest

I need assistance in constructing a test that specifically selects an element utilizing the data-testid attribute. The scenario involves a BaseTile component structured as follows: <template> <div data-testid="base-tile-icon&quo ...

Preventing event propagation in jQuery's click handler

Is it possible to prevent the propagation of a click event on a dynamically created div within its parent div? HTML <div id = "parent"> Some Stuff </div> Jquery $('#parent').click(function(){ $(this).append('<div class = ...

Accessing the value returned by an asynchronous function in Node.js with Electron

As I embark on a new project, my goal is to take user input, process it through a function, and then return the updated value back to the user. Despite being a novice with async functions, I've done extensive research but still can't pinpoint if ...

Using the index in Vue.js to locate a method within an HTML file

Currently, I am attempting to make use of the reference "index" located within <tr v-for="(note, index) in noteList" v-bind:key="index" in order to call shareToPublic(index). The method selectedID() allows for the selection of the ...

What is the best method for adjusting the text size within a doughnut chart using react-chartjs-2?

Is there a way to adjust the text size within the doughnut chart using react-chartjs-2? I find that the center text appears too small. https://i.stack.imgur.com/QsI0V.png import React, {Fragment} from 'react'; import Chart from 'chart.js&a ...

It appears that when importing from a shared package in lerna, the name must include "src" at the end for Typescript or Javascript files

I am currently working on a straightforward lerna project structure as shown below: Project | +-- packages | | | +-- shared | | | | | +-- src | | | | | +-- index.ts | | +-- someDir | | | +-- usesShared | ...

Navigating through Objects in Angular 9

I am facing a challenge in Angular 9/Typescript while trying to iterate through the object response from my JSON data. Despite searching for solutions, I haven't found any that work for me. In my JSON, there is a section called "details" which contain ...

How can I use PHP and JavaScript to iterate through a <select> / <option> list and gather the values?

I am working on a project where I have a group of options within a selection dropdown and my goal is to utilize JavaScript to deselect all chosen values, gather them into a string, and then send it over to my PHP script. ...