I have problem with updating v-for when I push new data to playlistTracks array. I'm passing playlistTracks from App.vue through playlist.vue to trackList.vue and once I run add()
function which pushes new object to playlistTracks but it doesn't get rendered to site. In Vue devtools I can see it got passed with new data to trackList.vue but v-for just doesn't update and render it on site.
App.vue
<script setup>
import Playlist from './components/playlist.vue';
let playlistTracks = [
{
name: 'name4',
artist: 'artist4',
album: 'album4',
id: 4,
},
{
name: 'name5',
artist: 'artist5',
album: 'album5',
id: 5,
},
];
function add() {
const track = {
name: 'newTrack',
artist: 'newArtist',
album: 'new Album',
id: 10,
};
playlistTracks.push(track);
}
</script>
<template>
<div>
<h1>Ja<span >mmm</span>ing</h1>
<div >
<button @click="add">Test add</button>
<div >
<Playlist
:playlistTracks="playlistTracks"
/>
</div>
</div>
</div>
</template>
playlist.vue
<script setup>
import TrackList from './trackList.vue';
const props = defineProps({
playlistName: String,
playlistTracks: Array,
});
</script>
<template>
<div >
<input value="New Playlist" />
<TrackList :tracks="props.playlistTracks" :isRemoval="true" />
<button >SAVE TO SPOTIFY</button>
</div>
</template>
trackList.vue
<script setup>
import Track from './track.vue';
const props = defineProps({
tracks: Array,
isRemoval: Boolean,
});
let tracksList = props.tracks;
</script>
<template>
<div >
<Track
v-for="track in tracksList"
:track="track"
:key="track.id"
:isRemoval="props.isRemoval"
/>
</div>
</template>
CodePudding user response:
It looks like you're using the Composition API for single file components. An important note is that in the official Vue.js documentation, there is important information regarding references to reactive data:
Reactive state needs to be explicitly created using Reactivity APIs. Similar to values returned from a
setup()
function, refs are automatically unwrapped when referenced in templates:
This means that your array is most likely not retaining reactivity, so even if the array itself successfully updates, Vue is not detecting these changes so it can know to re-render!
The examples they give for resolving this are as follows:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count ">{{ count }}</button>
</template>
const obj = reactive({ count: 0 })
From this, we can come to the likely conclusion that your solution will consist of two different changes. First, to your App.vue
file, wrap the array in the reactive()
call:
/*...*/
import { reactive } from vue;
let playlistTracks = reactive([
{
name: 'name4',
artist: 'artist4',
album: 'album4',
id: 4,
},
{
name: 'name5',
artist: 'artist5',
album: 'album5',
id: 5,
},
]);
/*...*/
Then, simply remove the unnecessary additional property in trackList.vue
:
<script setup>
import Track from './track.vue';
const props = defineProps({
tracks: Array,
isRemoval: Boolean,
});
</script>
<template>
<div >
<Track
v-for="track in props.tracks"
:track="track"
:key="track.id"
:isRemoval="props.isRemoval"
/>
</div>
</template>
Disclaimer: I am inexperienced with single file components, and especially with the Composition API, so some testing of the above solution will be required.
CodePudding user response:
To maintain reactivity
in Vue 3 composition API you have to wrap your data in either ref
or reactive
otherwise Vue will not detect any changes. ref can be used for both primitive and non-primitive while reactive can only be used for non-primitive types. Just wrap your playlistTracks data in ref and the component should rerender on any change within playlistTracks.
import {ref} from 'vue';
const playlistTracks = ref([
{
name: 'name4',
artist: 'artist4',
album: 'album4',
id: 4,
},
{
name: 'name5',
artist: 'artist5',
album: 'album5',
id: 5,
},
])