mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 07:11:12 -05:00
feat: Implement comprehensive re-render optimizations
This commit is contained in:
@@ -494,14 +494,28 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'replace':
|
case 'replace':
|
||||||
// Full refresh: replace entire queue
|
// Smart refresh: only update if data actually changed
|
||||||
setItems(moderationItems);
|
const mergeResult = smartMergeArray(itemsRef.current, moderationItems, {
|
||||||
|
compareFields: ['status', 'content', 'reviewed_at', 'reviewed_by', 'reviewer_notes', 'assigned_to', 'locked_until'],
|
||||||
|
preserveOrder: false,
|
||||||
|
addToTop: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mergeResult.hasChanges) {
|
||||||
|
setItems(mergeResult.items);
|
||||||
|
console.log('🔄 Queue updated (replace mode):', {
|
||||||
|
added: mergeResult.changes.added.length,
|
||||||
|
removed: mergeResult.changes.removed.length,
|
||||||
|
updated: mergeResult.changes.updated.length,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('✅ Queue unchanged (replace mode) - no visual updates needed');
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentPreserveInteraction) {
|
if (!currentPreserveInteraction) {
|
||||||
// Reset interaction state if not preserving
|
|
||||||
setPendingNewItems([]);
|
setPendingNewItems([]);
|
||||||
setNewItemsCount(0);
|
setNewItemsCount(0);
|
||||||
}
|
}
|
||||||
console.log('🔄 Queue replaced (replace mode) - full refresh with', moderationItems.length, 'items');
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -821,7 +835,15 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
|||||||
if (wasInQueue && !shouldBeInQueue) {
|
if (wasInQueue && !shouldBeInQueue) {
|
||||||
// Submission moved out of current filter (e.g., pending → approved)
|
// Submission moved out of current filter (e.g., pending → approved)
|
||||||
console.log('❌ Submission moved out of queue:', updatedSubmission.id);
|
console.log('❌ Submission moved out of queue:', updatedSubmission.id);
|
||||||
setItems(prev => prev.filter(i => i.id !== updatedSubmission.id));
|
setItems(prev => {
|
||||||
|
const exists = prev.some(i => i.id === updatedSubmission.id);
|
||||||
|
if (!exists) {
|
||||||
|
console.log('✅ Realtime: Item already removed', updatedSubmission.id);
|
||||||
|
return prev; // Keep existing array reference
|
||||||
|
}
|
||||||
|
console.log('❌ Realtime: Removing item from queue', updatedSubmission.id);
|
||||||
|
return prev.filter(i => i.id !== updatedSubmission.id);
|
||||||
|
});
|
||||||
} else if (shouldBeInQueue) {
|
} else if (shouldBeInQueue) {
|
||||||
// Submission should be in queue - update it
|
// Submission should be in queue - update it
|
||||||
console.log('🔄 Submission updated in queue:', updatedSubmission.id);
|
console.log('🔄 Submission updated in queue:', updatedSubmission.id);
|
||||||
@@ -870,10 +892,32 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
|||||||
// Update or add to queue
|
// Update or add to queue
|
||||||
setItems(prev => {
|
setItems(prev => {
|
||||||
const exists = prev.some(i => i.id === fullItem.id);
|
const exists = prev.some(i => i.id === fullItem.id);
|
||||||
|
|
||||||
if (exists) {
|
if (exists) {
|
||||||
|
// Check if item actually changed before updating
|
||||||
|
const currentItem = prev.find(i => i.id === fullItem.id);
|
||||||
|
if (!currentItem) return prev;
|
||||||
|
|
||||||
|
// Deep comparison of critical fields
|
||||||
|
const hasChanged =
|
||||||
|
currentItem.status !== fullItem.status ||
|
||||||
|
currentItem.reviewed_at !== fullItem.reviewed_at ||
|
||||||
|
currentItem.reviewed_by !== fullItem.reviewed_by ||
|
||||||
|
currentItem.reviewer_notes !== fullItem.reviewer_notes ||
|
||||||
|
currentItem.assigned_to !== fullItem.assigned_to ||
|
||||||
|
currentItem.locked_until !== fullItem.locked_until ||
|
||||||
|
JSON.stringify(currentItem.content) !== JSON.stringify(fullItem.content);
|
||||||
|
|
||||||
|
if (!hasChanged) {
|
||||||
|
console.log('✅ Realtime UPDATE: No changes detected for', fullItem.id);
|
||||||
|
return prev; // Keep existing array reference
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🔄 Realtime UPDATE: Changes detected for', fullItem.id);
|
||||||
return prev.map(i => i.id === fullItem.id ? fullItem : i);
|
return prev.map(i => i.id === fullItem.id ? fullItem : i);
|
||||||
} else {
|
} else {
|
||||||
return [fullItem, ...prev]; // Add at top
|
console.log('🆕 Realtime UPDATE: Adding new item', fullItem.id);
|
||||||
|
return [fullItem, ...prev];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -643,16 +643,32 @@ export const QueueItem = memo(({
|
|||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}, (prevProps, nextProps) => {
|
}, (prevProps, nextProps) => {
|
||||||
// Custom comparison to prevent re-renders
|
// Quick checks first (cheapest)
|
||||||
return (
|
if (prevProps.item.id !== nextProps.item.id) return false;
|
||||||
prevProps.item.id === nextProps.item.id &&
|
if (prevProps.item.status !== nextProps.item.status) return false;
|
||||||
prevProps.item.status === nextProps.item.status &&
|
if (prevProps.actionLoading !== nextProps.actionLoading) return false;
|
||||||
prevProps.actionLoading === nextProps.actionLoading &&
|
if (prevProps.currentLockSubmissionId !== nextProps.currentLockSubmissionId) return false;
|
||||||
prevProps.lockedSubmissions.has(prevProps.item.id) === nextProps.lockedSubmissions.has(nextProps.item.id) &&
|
if (prevProps.notes[prevProps.item.id] !== nextProps.notes[nextProps.item.id]) return false;
|
||||||
prevProps.currentLockSubmissionId === nextProps.currentLockSubmissionId &&
|
if (prevProps.notes[`reverse-${prevProps.item.id}`] !== nextProps.notes[`reverse-${nextProps.item.id}`]) return false;
|
||||||
prevProps.notes[prevProps.item.id] === nextProps.notes[nextProps.item.id] &&
|
|
||||||
prevProps.notes[`reverse-${prevProps.item.id}`] === nextProps.notes[`reverse-${nextProps.item.id}`]
|
// Check lock status
|
||||||
);
|
const prevLocked = prevProps.lockedSubmissions.has(prevProps.item.id);
|
||||||
|
const nextLocked = nextProps.lockedSubmissions.has(nextProps.item.id);
|
||||||
|
if (prevLocked !== nextLocked) return false;
|
||||||
|
|
||||||
|
// Deep comparison of content and other fields that affect rendering
|
||||||
|
if (prevProps.item.reviewed_at !== nextProps.item.reviewed_at) return false;
|
||||||
|
if (prevProps.item.reviewed_by !== nextProps.item.reviewed_by) return false;
|
||||||
|
if (prevProps.item.reviewer_notes !== nextProps.item.reviewer_notes) return false;
|
||||||
|
if (prevProps.item.assigned_to !== nextProps.item.assigned_to) return false;
|
||||||
|
if (prevProps.item.locked_until !== nextProps.item.locked_until) return false;
|
||||||
|
if (prevProps.item.escalated !== nextProps.item.escalated) return false;
|
||||||
|
|
||||||
|
// Content comparison (most expensive, do last)
|
||||||
|
if (JSON.stringify(prevProps.item.content) !== JSON.stringify(nextProps.item.content)) return false;
|
||||||
|
|
||||||
|
// All checks passed - items are identical
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
QueueItem.displayName = 'QueueItem';
|
QueueItem.displayName = 'QueueItem';
|
||||||
|
|||||||
Reference in New Issue
Block a user