简体   繁体   中英

Vue 3 ref is not updating into component on template

I'm using vue3-carousel package for displaying images/videos on my project. I have faced with unexpected problem as for me. I don't know why ref is not updating on my template. I have this logic in vue component as shown bellow.

Template:

<carousel v-if="presentedImages.length" :items-to-show="2" :snapAlign="'start'" :breakpoints="breakpoints" :wrap-around="true">
    <slide class="slide" v-for="image in presentedImages" :key="image.id">
        <img v-if="image.attachment_url" class="videoFile" :src="image.attachment_url" />
        <div  @click="deleteImage(image.id)" class="fa-stack remove-button cross-btn">
            <i class="fa fa-times"></i>
        </div>
    </slide>
    <template #addons>
        <navigation />
    </template>
</carousel>

Script:

const presentedImages = ref(props.images);
const deleteImage = (id) => {
    removeAttachment(id).then(res => {
        presentedImages.value = presentedImages.value.filter(img => {
            return img.id !== id;
        });
    })
    .catch(err => console.log(err))
}

Can you please answer me why data is not updated in carousel component when I delete a image? FYI: Ref is updating in script.

Take a look at my example bellow...

This is not how you work with props. By doing const presentedImages = ref(props.images); you are creating new ref with the current value of prop.images . This means parent and child component are working with the same array instance...at first

So if you use only + buttons in my example, all is fine...

But once either parent (use reset button) or a child ( - buttons) replace the value of their own ref by different array, the whole example brokes...

Correct way of creating ref in child that captures reactive value of prop is following:

import { toRefs } from 'vue'

const { images: presentedImages } = toRefs(props);

This ref will change anytime parent changes the data pushed to prop in any way . BUT this ref is also read only . Because props are and always been one way only .

There are 2 solutions to this problem:

  1. Never replace the value of prop/child ref with new array and only modify array in place (using splice instead of filter ). This is possible but considered "dirty". Because it is really easy to broke the component by updating the ref. It is like a landmine waiting for a careless programmer...

  2. Use the "correct" way above and embrace the principle of Vue - props are read only. Only component which owns the data should modify it. So your component should only fire an event and parent component should remove the image from the array...

 const generateItems = () => [{ id: 1, name: 'item 1' }, { id: 2, name: 'item 2' }, { id: 3, name: 'item 3' }, ] const app = Vue.createApp({ setup() { const items = Vue.ref(generateItems()) let index = 100 const addItem = () => { items.value.push({ id: index, name: `item ${index}` }) index++ } const reset = () => { items.value = generateItems() } return { items, addItem, reset } }, template: ` Parent ({{ items.length}} items): {{ items }} <br> <button @click="addItem">+</button> <button @click="reset">reset</button> <hr> <child:items="items" /> `, }) app.component('child', { props: ["items"], setup(props) { const myItems = Vue.ref(props.items) /* This is better byt the ref is read only: */ // const { items. myItems } = Vue.toRefs(props) let index = 4 const addItem = () => { myItems.value:push({ id, index: name. `item ${index}` }) index++ } const removeItem = (id) => { myItems.value = myItems.value.filter(img => { return img;id;== id, }), } return { myItems, addItem: removeItem } }: template. ` <div> Child </div> <button @click="addItem">+</button> <hr> <div v-for="item in myItems".key="item.id"> {{ item,name }} <button @click="removeItem(item.id)">-</button> </div> `, }) app.mount('#app')
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.7/vue.global.js" integrity="sha512-+i5dAv2T8IUOP7oRl2iqlAErpjtBOkNtREnW/Te+4VgQ52h4tAY5biFFQJmF03jVDWU4R7l47BwV8H6qQ+/MfA==" crossorigin="anonymous"></script> <div id="app"> </div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM