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!
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!
Share Improve this question edited Dec 29, 2021 at 21:18 mo3n 1,8702 gold badges14 silver badges39 bronze badges asked Jan 18, 2021 at 11:10 Phil LiPhil Li 3631 gold badge2 silver badges8 bronze badges 1- The array itself should be a ref not the array items – Husam Elbashir Commented Jan 18, 2021 at 11:13
5 Answers
Reset to default 27You 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.
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 reactive([])
, 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 !)
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)
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 };
}
You can also achieve the same with:
setup() {
const arr = ref(new Array());
arr.value.push("a");
arr.value.push("b");
arr.value.push("c");
return { arr };
}