mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 16:27:18 +02:00
Memoize TimelineList in CommentThread to prevent re-render on typing
The body state (composer text) was causing the entire CommentThread to re-render on every keystroke, including the TimelineList with 143+ items. This was the primary cause of typing lag (110ms average per keystroke, 60 long tasks totaling 3.7s of blocking). Wrap TimelineList in useMemo so it only re-renders when timeline data actually changes. Convert handleFeedbackVote to useCallback so it doesn't defeat the memoization. Before: 110ms avg, 181ms P95, 60 long tasks After: 21ms avg, 35ms P95, 5 long tasks Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
96f72be88e
commit
67cda7f3cd
1 file changed
+33
-18
@@ -1,4 +1,4 @@
|
||||
import { memo, useEffect, useMemo, useRef, useState, type ChangeEvent } from "react";
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState, type ChangeEvent } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import type {
|
||||
Agent,
|
||||
@@ -728,26 +728,27 @@ export const CommentThread = memo(function CommentThread({
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFeedbackVote(
|
||||
commentId: string,
|
||||
vote: FeedbackVoteValue,
|
||||
options?: { allowSharing?: boolean; reason?: string },
|
||||
) {
|
||||
if (!onVote) return;
|
||||
setVotingTargetId(commentId);
|
||||
try {
|
||||
await onVote(commentId, vote, options);
|
||||
} finally {
|
||||
setVotingTargetId(null);
|
||||
}
|
||||
}
|
||||
const handleFeedbackVote = useCallback(
|
||||
async (
|
||||
commentId: string,
|
||||
vote: FeedbackVoteValue,
|
||||
options?: { allowSharing?: boolean; reason?: string },
|
||||
) => {
|
||||
if (!onVote) return;
|
||||
setVotingTargetId(commentId);
|
||||
try {
|
||||
await onVote(commentId, vote, options);
|
||||
} finally {
|
||||
setVotingTargetId(null);
|
||||
}
|
||||
},
|
||||
[onVote],
|
||||
);
|
||||
|
||||
const canSubmit = !submitting && !!body.trim();
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-semibold">Timeline ({timeline.length + queuedComments.length})</h3>
|
||||
|
||||
const timelineSection = useMemo(
|
||||
() => (
|
||||
<TimelineList
|
||||
timeline={timeline}
|
||||
agentMap={agentMap}
|
||||
@@ -761,6 +762,20 @@ export const CommentThread = memo(function CommentThread({
|
||||
highlightCommentId={highlightCommentId}
|
||||
feedbackTermsUrl={feedbackTermsUrl}
|
||||
/>
|
||||
),
|
||||
[
|
||||
timeline, agentMap, currentUserId, companyId, projectId,
|
||||
feedbackVoteByTargetId, feedbackDataSharingPreference,
|
||||
onVote, handleFeedbackVote, votingTargetId, highlightCommentId,
|
||||
feedbackTermsUrl,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-semibold">Timeline ({timeline.length + queuedComments.length})</h3>
|
||||
|
||||
{timelineSection}
|
||||
|
||||
{liveRunSlot}
|
||||
|
||||
|
||||
Reference in new issue
Block a user