最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Vue 3, composition API, Array of refs doesn't work - Stack Overflow

programmeradmin13浏览0评论

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
Add a comment  | 

5 Answers 5

Reset to default 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.

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 };
}
发布评论

评论列表(0)

  1. 暂无评论