Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue 3, composition API, Array of refs doesn't work

Please see below code.

<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>

And the output is below

{ "_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

I have to call item.value in the template to make it work. What's the work around for this scenario in vue3?

Cheers!

like image 961
Phil Li Avatar asked Jan 18 '21 11:01

Phil Li


People also ask

What is Vue 3 reactivity with composition API?

Thankfully, Vue 3 Reactivity with Composition API is equally robust. Vue 3 Reactivity with Composition API is driven by reactive () and ref () functions. These functions turn the component model data reactive so that Vue is able to track changes. However, both reactive () and ref () have their specific use-cases.

Can I add ref items to a normal array in vue3?

Bookmark this question. Show activity on this post. Please see below code. I have to call item.value in the template to make it work. What's the work around for this scenario in vue3? Cheers! Show activity on this post. There is no point adding ref items to a normal array. The Array itself should be ref.

Does Vue V3 still support the old component syntax?

In the end, thanks to the huge developer outcry the initial plan to deprecate the old component syntax was abandoned, so v3 will still support the old Vue syntax we love. Yay! The Composition API came about as a solution to the age-old problem of code reuse and encapsulation of business logic.

How to work with template refs in composition API?

So, how can you work with template refs in Composition API? It might be more simple than you think! The thing is, Vue.js unifies the concept of refs, meaning that you just need to use the ref () function you already know for declaring reactive state variables in order to declare a template ref as well.


4 Answers

You are doing it wrong; try following

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

There is no point adding ref items to a normal array. The Array itself should be ref.

like image 88
ashwin bande Avatar answered Oct 24 '22 00:10

ashwin bande


Some information about using array with ref() and reactive() which may be helpful.

Recently, I am learning composition API by developing a simple todo list app. I ran into some problems when dealing with array by using ref() and reactive() and found some behaviors which may be helpful for folks who are learning composition API too, so I wrote down some words here. If there is something wrong, please tell me!

1. What is the problem when I use reactive() to deal with array?

So...at first everything just work as I expected until I working on developing delete function.

I tried to build a button which will trigger the deleteHandler function when it been click. And the deleteHandler would filter out the element in todos:

Here is my code:

<template>
    <div>
        <h1>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, I face a crucial problem, since the filter function would not mutate the original value but return a new value. Vue could not detect the change inside todos.

To solve this problem, I rewrite my code. Instead of assigning todos to reactice([]), I warpped the array with object like this -> reactive({todos:[]}). And it works !

<template>
    <div>
        <h1>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 seems that vue could only watch on the change with same reference(object in JavaScript is called by reference), but could not detect the change when the reference is changed. As a resault, I think "wrap the array inside object" is a better way to deal with array in composition API.

2. ref() for primitive value and reactive() value?

According to the most information we could found, It seems that we can make a conclusion:

ref() for primitive value and reactive() value

However, if we write some code like this, Vue is still able to detect the change inside it:

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

return{
 ...toRefs(obj)
}

The reason is that when we pass data into ref(), it would first check whether the data been sended is primitive or object. If it is object, ref() would call reactive() to deal with it.In other words, reactive() is the one who actually take on the job behind the scene.

little conclusion

At this stage, it seems that we can use ref() anytime. However, I think it's better to use reactive() for object and ref() for primitive to make difference!(If you have any ideas about this topic, please share it to me !)

like image 3
蔡恂藝 Avatar answered Oct 24 '22 00:10

蔡恂藝


This is the correct answer

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

This option is possible, but the first is much better.

const arr = reactive([]);
      arr.push("a")
      arr.push("b")
      arr.push("c")
      console.log(arr)
like image 2
Oleksii Zelenko Avatar answered Oct 23 '22 22:10

Oleksii Zelenko


They should be accessed using value field :

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

but this is a bad practice, your should define your array as ref then push values to it :

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

another crafted solution is to init the array with that values :

  setup() {
    const arr = ref(["a","b","c"]);
   
    return { arr };
  }
like image 2
Boussadjra Brahim Avatar answered Oct 23 '22 22:10

Boussadjra Brahim