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