Skip to content

Commit 3df0fd4

Browse files
chore: memoize components (vercel#607)
1 parent 906d7d3 commit 3df0fd4

File tree

6 files changed

+123
-53
lines changed

6 files changed

+123
-53
lines changed

components/block-actions.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,10 @@ function PureBlockActions({
9797
}
9898

9999
export const BlockActions = memo(PureBlockActions, (prevProps, nextProps) => {
100-
if (
101-
prevProps.block.status === 'streaming' &&
102-
nextProps.block.status === 'streaming'
103-
) {
104-
return true;
105-
}
100+
if (prevProps.block.status !== nextProps.block.status) return false;
101+
if (prevProps.currentVersionIndex !== nextProps.currentVersionIndex)
102+
return false;
103+
if (prevProps.isCurrentVersion !== nextProps.isCurrentVersion) return false;
106104

107-
return false;
105+
return true;
108106
});

components/message-actions.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import {
1414
TooltipProvider,
1515
TooltipTrigger,
1616
} from './ui/tooltip';
17+
import { memo } from 'react';
18+
import equal from 'fast-deep-equal';
1719

18-
export function MessageActions({
20+
export function PureMessageActions({
1921
chatId,
2022
message,
2123
vote,
@@ -164,3 +166,13 @@ export function MessageActions({
164166
</TooltipProvider>
165167
);
166168
}
169+
170+
export const MessageActions = memo(
171+
PureMessageActions,
172+
(prevProps, nextProps) => {
173+
if (!equal(prevProps.vote, nextProps.vote)) return false;
174+
if (prevProps.isLoading !== nextProps.isLoading) return false;
175+
176+
return true;
177+
},
178+
);

components/message.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,16 @@ export const PreviewMessage = memo(
223223
PurePreviewMessage,
224224
(prevProps, nextProps) => {
225225
if (prevProps.isLoading !== nextProps.isLoading) return false;
226-
if (prevProps.isLoading && nextProps.isLoading) return false;
227-
if (prevProps.message.content && nextProps.message.content) return false;
226+
if (prevProps.message.content !== nextProps.message.content) return false;
227+
if (
228+
!equal(
229+
prevProps.message.toolInvocations,
230+
nextProps.message.toolInvocations,
231+
)
232+
)
233+
return false;
228234
if (!equal(prevProps.vote, nextProps.vote)) return false;
235+
229236
return true;
230237
},
231238
);

components/messages.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Overview } from './overview';
55
import { UIBlock } from './block';
66
import { Dispatch, memo, SetStateAction } from 'react';
77
import { Vote } from '@/lib/db/schema';
8+
import equal from 'fast-deep-equal';
89

910
interface MessagesProps {
1011
chatId: string;
@@ -74,15 +75,11 @@ function PureMessages({
7475
);
7576
}
7677

77-
function areEqual(prevProps: MessagesProps, nextProps: MessagesProps) {
78-
if (
79-
prevProps.block.status === 'streaming' &&
80-
nextProps.block.status === 'streaming'
81-
) {
82-
return true;
83-
}
78+
export const Messages = memo(PureMessages, (prevProps, nextProps) => {
79+
if (prevProps.isLoading !== nextProps.isLoading) return false;
80+
if (prevProps.isLoading && nextProps.isLoading) return false;
81+
if (prevProps.messages.length !== nextProps.messages.length) return false;
82+
if (!equal(prevProps.votes, nextProps.votes)) return false;
8483

85-
return false;
86-
}
87-
88-
export const Messages = memo(PureMessages, areEqual);
84+
return true;
85+
});

components/multimodal-input.tsx

Lines changed: 84 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -247,40 +247,16 @@ function PureMultimodalInput({
247247
/>
248248

249249
{isLoading ? (
250-
<Button
251-
className="rounded-full p-1.5 h-fit absolute bottom-2 right-2 m-0.5 border dark:border-zinc-600"
252-
onClick={(event) => {
253-
event.preventDefault();
254-
stop();
255-
setMessages((messages) => sanitizeUIMessages(messages));
256-
}}
257-
>
258-
<StopIcon size={14} />
259-
</Button>
250+
<StopButton stop={stop} setMessages={setMessages} />
260251
) : (
261-
<Button
262-
className="rounded-full p-1.5 h-fit absolute bottom-2 right-2 m-0.5 border dark:border-zinc-600"
263-
onClick={(event) => {
264-
event.preventDefault();
265-
submitForm();
266-
}}
267-
disabled={input.length === 0 || uploadQueue.length > 0}
268-
>
269-
<ArrowUpIcon size={14} />
270-
</Button>
252+
<SendButton
253+
input={input}
254+
submitForm={submitForm}
255+
uploadQueue={uploadQueue}
256+
/>
271257
)}
272258

273-
<Button
274-
className="rounded-full p-1.5 h-fit absolute bottom-2 right-11 m-0.5 dark:border-zinc-700"
275-
onClick={(event) => {
276-
event.preventDefault();
277-
fileInputRef.current?.click();
278-
}}
279-
variant="outline"
280-
disabled={isLoading}
281-
>
282-
<PaperclipIcon size={14} />
283-
</Button>
259+
<AttachmentsButton fileInputRef={fileInputRef} isLoading={isLoading} />
284260
</div>
285261
);
286262
}
@@ -295,3 +271,80 @@ export const MultimodalInput = memo(
295271
return true;
296272
},
297273
);
274+
275+
function PureAttachmentsButton({
276+
fileInputRef,
277+
isLoading,
278+
}: {
279+
fileInputRef: React.MutableRefObject<HTMLInputElement | null>;
280+
isLoading: boolean;
281+
}) {
282+
return (
283+
<Button
284+
className="rounded-full p-1.5 h-fit absolute bottom-2 right-11 m-0.5 dark:border-zinc-700"
285+
onClick={(event) => {
286+
event.preventDefault();
287+
fileInputRef.current?.click();
288+
}}
289+
variant="outline"
290+
disabled={isLoading}
291+
>
292+
<PaperclipIcon size={14} />
293+
</Button>
294+
);
295+
}
296+
297+
const AttachmentsButton = memo(PureAttachmentsButton);
298+
299+
function PureStopButton({
300+
stop,
301+
setMessages,
302+
}: {
303+
stop: () => void;
304+
setMessages: Dispatch<SetStateAction<Array<Message>>>;
305+
}) {
306+
return (
307+
<Button
308+
className="rounded-full p-1.5 h-fit absolute bottom-2 right-2 m-0.5 border dark:border-zinc-600"
309+
onClick={(event) => {
310+
event.preventDefault();
311+
stop();
312+
setMessages((messages) => sanitizeUIMessages(messages));
313+
}}
314+
>
315+
<StopIcon size={14} />
316+
</Button>
317+
);
318+
}
319+
320+
const StopButton = memo(PureStopButton);
321+
322+
function PureSendButton({
323+
submitForm,
324+
input,
325+
uploadQueue,
326+
}: {
327+
submitForm: () => void;
328+
input: string;
329+
uploadQueue: Array<string>;
330+
}) {
331+
return (
332+
<Button
333+
className="rounded-full p-1.5 h-fit absolute bottom-2 right-2 m-0.5 border dark:border-zinc-600"
334+
onClick={(event) => {
335+
event.preventDefault();
336+
submitForm();
337+
}}
338+
disabled={input.length === 0 || uploadQueue.length > 0}
339+
>
340+
<ArrowUpIcon size={14} />
341+
</Button>
342+
);
343+
}
344+
345+
const SendButton = memo(PureSendButton, (prevProps, nextProps) => {
346+
if (prevProps.uploadQueue.length !== nextProps.uploadQueue.length)
347+
return false;
348+
if (!prevProps.input !== !nextProps.input) return false;
349+
return true;
350+
});

components/toolbar.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,5 +469,8 @@ const PureToolbar = ({
469469
};
470470

471471
export const Toolbar = memo(PureToolbar, (prevProps, nextProps) => {
472-
return equal(prevProps, nextProps);
472+
if (prevProps.isLoading !== nextProps.isLoading) return false;
473+
if (prevProps.isToolbarVisible !== nextProps.isToolbarVisible) return false;
474+
475+
return true;
473476
});

0 commit comments

Comments
 (0)