I am building a small app to sign up users to sing a karaoke music. I am using SWR to fetch info from a mongoDB and due to performance issues I am trying to implement optimist UI. Although, I can't seem to make it work...
When a song is played/singed is marked as such with the code below:
const { mutate } = useSWRConfig();
const isSongPlayed = async (song: Song, bool: boolean) => {
try {
const isTheSongPlayed = await fetch(`/api/songs/${song?._id}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ ...song, played: bool }),
});
const playedSong = await isTheSongPlayed.json();
if (!playedSong.success || !playedSong.data) setLoading(true);
// the information to be changed is fetch is this endpoint
mutate(`/api/events/${id}`);
} catch (error) {
console.log(error);
}
};
// this function is called from a button
const onSetAsPlayed = (song: Song) => {
song.played ? isSongPlayed(song, false) : isSongPlayed(song, true);
};
Everything works pretty well locally, although the deployed version is very slow because it's counting on revalidation (call to the server) to update the UI. The code above isn't optimistic.
The flow is following: singleEvent page:
- displays a component to all requests;
- displays a component to all moments:
- Each moment display the songs list.
The object that is passed look like this:
{
eventTitle: '',
moments: [
{
momentTitle: '',
songs: [
{
title: '',
artist: '',
played: boolean,
requests: [user object]
},
],
},
],
};
What I have been doing is:
const isSongPlayed = async (song: Song, bool: boolean) => {
/* all code necessary to update the object above
which is basically marked the song as played and
pass it to the object again */
mutate(`/api/events/${id}`, newObjectUpdated, false);
try {
const isTheSongPlayed = await fetch(`/api/songs/${song?._id}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ ...song, played: bool }),
});
const playedSong = await isTheSongPlayed.json();
if (!playedSong.success || !playedSong.data) setLoading(true);
mutate(`/api/events/${id}`, newObjectUpdated, true);
} catch (error) {
console.log(error);
}
};
The code above works poorly, basically the updated song disappears and appears again with the changes which is a really bad UI.
Thanks in advance.
CodePudding user response:
So I managed to fix it:
const isSongPlayed = async (song: Song, bool: boolean) => {
await fetch(`/api/songs/${song?._id}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ ...song, played: bool }),
});
const filterMoments = singleEvent.moments.filter((moment: any) => {
return moment._id !== momentData._id;
});
const filterSongs = momentData.songs.filter((s: any) => {
return s._id !== song._id;
});
const songUpdated = [...filterSongs, { ...song, played: bool }].sort(
(a, b) => {
if (a.createdAt < b.createdAt) {
return -1;
}
if (a.createdAt > b.createdAt) {
return 1;
}
return 0;
}
);
const momentsUpdated = [
...filterMoments,
{ ...momentData, songs: songUpdated },
].sort((a, b) => {
return a.index - b.index;
});
const eventUpdated = { ...singleEvent, moments: momentsUpdated };
mutate(`/api/events/${momentData.event}`, eventUpdated, false);
};
What happened is that I was revalidating the data and the event moments and songs wasn't sorted and originally, after a few time I figured out the problem. Not sure if it's the best solution, but seems serving the porpuse right.