refactor: memoize real-time RSVP update handler for improved stability and performance.

This commit is contained in:
2026-01-27 02:19:57 +02:00
parent 59a57f0f6f
commit 5e6799677b

View File

@@ -693,9 +693,8 @@ export function AdminDashboard({
});
}, [magicLinkDialog, toast]);
// Real-time updates
useAdminRealtime({
onRSVPUpdate: (event) => {
// Memoized RSVP update handler for stable reference in real-time subscription
const handleRSVPUpdate = useCallback((event: any) => {
if (event.action === "create" || event.action === "update") {
// Refresh both RSVP data and guest list to sync status
Promise.all([
@@ -721,7 +720,28 @@ export function AdminDashboard({
console.warn('[AdminDashboard] Failed to refresh RSVP data:', error);
}),
// Refresh guest list to sync RSVP status - CRITICAL for Master Guest List sync
refreshGuestList(),
fetch(`/api/admin/guests?includeCheckIn=true&page=${currentPage}&limit=${pageSize}`, {
credentials: 'include'
})
.then(res => res.json())
.then(guestsData => {
if (guestsData.pagination) {
setGuests(guestsData.guests || []);
setTotalGuests(guestsData.pagination.total || 0);
setTotalPages(guestsData.pagination.totalPages || 1);
} else if (Array.isArray(guestsData)) {
setGuests(guestsData);
setTotalGuests(guestsData.length);
setTotalPages(1);
} else if (Array.isArray(guestsData.guests)) {
setGuests(guestsData.guests);
setTotalGuests(guestsData.pagination?.total || guestsData.guests.length);
setTotalPages(guestsData.pagination?.totalPages || 1);
}
})
.catch(error => {
console.error('[AdminDashboard] Failed to refresh guest list:', error);
}),
]).then(() => {
// Only show toast for create actions to avoid spam on updates
if (event.action === "create") {
@@ -735,15 +755,41 @@ export function AdminDashboard({
console.warn('[AdminDashboard] Failed to refresh data after RSVP update:', error);
});
}
},
onGuestUpdate: (event) => {
}, [currentPage, pageSize, toast]);
// Memoized guest update handler for stable reference
const handleGuestUpdate = useCallback((event: any) => {
// Also refresh guest list when guest data is updated directly
if (event.action === "update") {
refreshGuestList().catch(error => {
fetch(`/api/admin/guests?includeCheckIn=true&page=${currentPage}&limit=${pageSize}`, {
credentials: 'include'
})
.then(res => res.json())
.then(guestsData => {
if (guestsData.pagination) {
setGuests(guestsData.guests || []);
setTotalGuests(guestsData.pagination.total || 0);
setTotalPages(guestsData.pagination.totalPages || 1);
} else if (Array.isArray(guestsData)) {
setGuests(guestsData);
setTotalGuests(guestsData.length);
setTotalPages(1);
} else if (Array.isArray(guestsData.guests)) {
setGuests(guestsData.guests);
setTotalGuests(guestsData.pagination?.total || guestsData.guests.length);
setTotalPages(guestsData.pagination?.totalPages || 1);
}
})
.catch(error => {
console.warn('[AdminDashboard] Failed to refresh guest list after guest update:', error);
});
}
},
}, [currentPage, pageSize]);
// Real-time updates with memoized handlers
useAdminRealtime({
onRSVPUpdate: handleRSVPUpdate,
onGuestUpdate: handleGuestUpdate,
onCheckinUpdate: (event) => {
if (event.action === "create") {
const guestId = event.data?.guestId as string | undefined;
@@ -1818,8 +1864,8 @@ export function AdminDashboard({
setNotifications(prev => prev.filter(n => n.id !== id));
};
// Helper function to refresh guest list
const refreshGuestList = async (page = currentPage) => {
// Helper function to refresh guest list - memoized for stable reference in real-time handlers
const refreshGuestList = useCallback(async (page = currentPage) => {
try {
const guestsRes = await fetch(`/api/admin/guests?includeCheckIn=true&page=${page}&limit=${pageSize}`, {
credentials: 'include'
@@ -1845,7 +1891,7 @@ export function AdminDashboard({
} catch (error) {
console.error('Failed to refresh guest list:', error);
}
};
}, [currentPage, pageSize]);
// Handle sending guest invitation
const handleSendInvite = async () => {
@@ -3929,8 +3975,7 @@ export function AdminDashboard({
<p className="text-xs text-wedding-moss/70">
by {photo.uploadedBy} {photo.time}
</p>
<span className={`text-xs px-2 py-1 rounded-full ${
photo.approved ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'
<span className={`text-xs px-2 py-1 rounded-full ${photo.approved ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'
}`}>
{photo.approved ? 'Approved' : 'Pending'}
</span>