19

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!

1
  • The array itself should be a ref not the array items Commented Jan 18, 2021 at 11:13

5 Answers 5

27

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.

Sign up to request clarification or add additional context in comments.

2 Comments

☝️ Since objects and arrays are both reference types and not primitives, one should use reactive instead of ref - or am I missing something?
@IrgendSonHansel The answer is "depends", I had a very specific case recently for ref instead of reactive on an array. Essentially, I needed it reference an array, but not the properties of the array, as custom getters would cause reactive to do strange things.
11

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

2 Comments

Please leave (a) link(s) to where you found these information.
Hi, to day I found something wrong with my understanding, so i edited it !
2

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)

Comments

2

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

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.