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

What is the best way to display items within a table using React?

I'm just starting to learn React. Can someone show me how to use the "map" function to list elements from two different arrays in two columns? state = { dates: ["2000", "2001", "2002"], cases: ["1", "2", "3"] } render() { return ( <thea ...

The best approach to incorporating interactive animation in next.js

My vision is to develop a character creation application using next js. The app should empower users to customize the character using sliders and gender selection buttons. The ultimate goal is to have a 2D animated version of the character that dynamicall ...

Utilizing Vue.js Datepicker to Specify a Date Range for Birth Date and Death Date

My goal is to disable all dates after today in both fields. Additionally, if a date of death is selected first, I want to disable all dates after that in the date of birth field. Here is what I have done so far: <Datepicker :disabled-dates="disabled ...

The Integration of Google Books API with Ajax Technology

When a user enters an ISBN number in the search box, it triggers a display of information from the Google Books API within a div. This information is fetched from a JSON file in the standard format and includes details like title, subtitle, author, and des ...

Using JQuery to extract information from a JSON file

Here is the code I am working on, where I pass input username and password values. The function I have written checks if the input matches the data in a JSON file. If there is a match, an alert saying "login correct" will be displayed, otherwise it will di ...

Tips for optimizing mobile performance by loading .obj models into objects on three.js

How can I properly load .obj models with objLoader and MTLLoader for my three.js mini game? I am facing an issue where the game loads fine on computers but fails to load on mobile browsers. Specifically, when accessed on a phone browser, the game attempts ...

Unexpected behavior: jQuery events failing to execute while triggering the 'change' event with fireEvent

Issue seen in IE7-8. Here's a simple illustration: <html> <head> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script> <script type="text/javas ...

"The authentication cookie fields are not defined when trying to get the authentication in the Express framework

After setting up my React client on port 3000 and Express on port 5000, I encountered an issue. When logging in, the cookie fields are set without any problems. However, when trying to retrieve the isauth value, it shows as undefined. //login log message ...

I am looking to incorporate automatic scrolling functionality into my RSS Feed

I'm in the process of developing an RSS feed for my website. My expertise in JS/jQuery is limited, so any assistance would be greatly appreciated. After utilizing Google's Feed API and creating my own RSS Reader Widget, I realized that it lacked ...

What sets apart using the loadText function from loadText() in JavaScript?

I've implemented a basic JS function that loads text lines into an unordered list. Javascript function loadText() { document.getElementById("text1").innerHTML = "Line 1"; document.getElementById("text2").innerHTML = "Line 2"; document.ge ...

Hiding content and troubleshooting video playback problems in FancyBox

I'm facing an interesting issue. I've implemented FancyBox lightbox to showcase a "video" tag when users click on the image thumbnail, and it functions well with all varieties of HTML5 video. The challenge arises when testing in browsers older th ...

What's the best method for uploading a file to AWS S3: POST or PUT requests?

Could you please provide insights on the advantages and disadvantages of utilizing POST versus PUT requests for uploading a file to Amazon Web Services S3? Although I have come across some relevant discussions on platforms like StackOverflow, such as this ...

Cease the execution of promises as soon as one promise is resolved

Using ES6 promises, I have created a function that iterates over an array of links to search for an image and stops once one is found. In the implementation of this function, the promise with the fastest resolution is executed while others continue to run ...

Ways to organize JSON information in Angular by date basis?

I am working on a project where I need to organize multiple JSON objects into an array based on their date data, with the date field serving as the key. ...

Material-UI slider in React keeps reverting back to zero value

Exploring the construction of an interface where selecting a radio option reveals distinct form elements. Once a slider is exposed, any value changes are stored in an object that is subsequently visible on the page. In this scenario, the object does get ...

Identify dead hyperlinks on a webpage with the help of selenium webdriver while steering clear of links that

I have been trying to identify broken links on a webpage by extracting all anchor tags. However, some of the links are dynamically generated through JavaScript. When I attempt to print out the list of all the links, I encounter a StaleElementReferenceExcep ...

Strange behavior occurs when a DOCTYPE is specified in XSLT and javascript interactions

Within our XLST, we utilize "vanilla" JavaScript to manipulate certain div elements within our reports. One particular div sits in the center of the screen and overlaps all other content. The code snippet below is used to position this div when the page lo ...

Retrieving information selectively using useSWRImmutable

Having issues fetching data using useSWRImmutable. The problem arises when attempting to display the fetched data inside the UserRow component. Even though I can successfully print the data outside of the UserRow component, any console.log() statements wi ...

The initial time delay set by setTimeout does not seem to have any impact

The issue with setTimeout not working as expected lies in its execution order. The code below it gets executed without waiting for the delay specified in the first argument of 'setTimeout' to run. (function() { var a = ['#bird',&ap ...

Navigating the FormSpree redirect: Tips and tricks

I recently set up my website on Github Pages and wanted to integrate a free contact form from FormSpree. However, I encountered an issue where after submitting the form, it redirected to a different website, which was not ideal. After researching online, I ...