mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-26 12:16:13 +01:00
🧵 fix: Prevent Unnecessary Re-renders when Loading Chats (#5189)
* chore: typing * chore: typing * fix: enhance message scrolling logic to handle empty messages tree and ref checks * fix: optimize message selection logic with useCallback for better performance * chore: typing * refactor: optimize icon rendering * refactor: further optimize chat props * fix: remove unnecessary console log in useQueryParams cleanup * refactor: add queryClient to reset message data on new conversation initiation * refactor: update data-testid attributes for consistency and improve code readability * refactor: integrate queryClient to reset message data on new conversation initiation
This commit is contained in:
parent
7987e04a2c
commit
b01c744eb8
20 changed files with 184 additions and 88 deletions
|
|
@ -1,26 +1,23 @@
|
|||
import React, { useMemo, memo } from 'react';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { TMessage, TPreset, Assistant, Agent } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import type { Assistant, Agent, TMessage } from 'librechat-data-provider';
|
||||
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
|
||||
import { getEndpointField, getIconEndpoint } from '~/utils';
|
||||
import Icon from '~/components/Endpoints/Icon';
|
||||
|
||||
const MessageIcon = memo(
|
||||
(
|
||||
props: Pick<TMessageProps, 'message' | 'conversation'> & {
|
||||
assistant?: Assistant;
|
||||
agent?: Agent;
|
||||
},
|
||||
) => {
|
||||
(props: {
|
||||
iconData?: TMessage & { modelLabel?: string };
|
||||
assistant?: Assistant;
|
||||
agent?: Agent;
|
||||
}) => {
|
||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
||||
const { message, conversation, assistant, agent } = props;
|
||||
const { iconData, assistant, agent } = props;
|
||||
|
||||
const assistantName = useMemo(() => assistant?.name ?? '', [assistant]);
|
||||
const assistantAvatar = useMemo(() => assistant?.metadata?.avatar ?? '', [assistant]);
|
||||
const agentName = useMemo(() => props.agent?.name ?? '', [props.agent]);
|
||||
const agentAvatar = useMemo(() => props.agent?.avatar?.filepath ?? '', [props.agent]);
|
||||
const isCreatedByUser = useMemo(() => message?.isCreatedByUser ?? false, [message]);
|
||||
|
||||
let avatarURL = '';
|
||||
|
||||
|
|
@ -30,21 +27,10 @@ const MessageIcon = memo(
|
|||
avatarURL = agentAvatar;
|
||||
}
|
||||
|
||||
const messageSettings = useMemo(
|
||||
() => ({
|
||||
...(conversation ?? {}),
|
||||
...({
|
||||
...(message ?? {}),
|
||||
iconURL: message?.iconURL ?? '',
|
||||
} as TMessage),
|
||||
}),
|
||||
[conversation, message],
|
||||
);
|
||||
|
||||
const iconURL = messageSettings.iconURL;
|
||||
const iconURL = iconData?.iconURL;
|
||||
const endpoint = useMemo(
|
||||
() => getIconEndpoint({ endpointsConfig, iconURL, endpoint: messageSettings.endpoint }),
|
||||
[endpointsConfig, iconURL, messageSettings.endpoint],
|
||||
() => getIconEndpoint({ endpointsConfig, iconURL, endpoint: iconData?.endpoint }),
|
||||
[endpointsConfig, iconURL, iconData?.endpoint],
|
||||
);
|
||||
|
||||
const endpointIconURL = useMemo(
|
||||
|
|
@ -52,10 +38,11 @@ const MessageIcon = memo(
|
|||
[endpointsConfig, endpoint],
|
||||
);
|
||||
|
||||
if (isCreatedByUser !== true && iconURL != null && iconURL.includes('http')) {
|
||||
if (iconData?.isCreatedByUser !== true && iconURL != null && iconURL.includes('http')) {
|
||||
return (
|
||||
<ConvoIconURL
|
||||
preset={messageSettings as typeof messageSettings & TPreset}
|
||||
iconURL={iconURL}
|
||||
modelLabel={iconData?.modelLabel}
|
||||
context="message"
|
||||
assistantAvatar={assistantAvatar}
|
||||
agentAvatar={agentAvatar}
|
||||
|
|
@ -68,10 +55,10 @@ const MessageIcon = memo(
|
|||
|
||||
return (
|
||||
<Icon
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
isCreatedByUser={iconData?.isCreatedByUser ?? false}
|
||||
endpoint={endpoint}
|
||||
iconURL={avatarURL || endpointIconURL}
|
||||
model={message?.model ?? conversation?.model}
|
||||
model={iconData?.model}
|
||||
assistantName={assistantName}
|
||||
agentName={agentName}
|
||||
size={28.8}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||
import type { TMessage, TMessageContentParts } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import Icon from '~/components/Chat/Messages/MessageIcon';
|
||||
import MessageIcon from '~/components/Chat/Messages/MessageIcon';
|
||||
import { useMessageHelpers, useLocalize } from '~/hooks';
|
||||
import ContentParts from './Content/ContentParts';
|
||||
import SiblingSwitch from './SiblingSwitch';
|
||||
|
|
@ -35,6 +36,26 @@ export default function Message(props: TMessageProps) {
|
|||
const fontSize = useRecoilValue(store.fontSize);
|
||||
const { children, messageId = null, isCreatedByUser } = message ?? {};
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: conversation?.endpoint,
|
||||
model: conversation?.model ?? message?.model,
|
||||
iconURL: conversation?.iconURL ?? message?.iconURL ?? '',
|
||||
modelLabel: conversation?.chatGptLabel ?? conversation?.modelLabel,
|
||||
isCreatedByUser: message?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
[
|
||||
conversation?.chatGptLabel,
|
||||
conversation?.modelLabel,
|
||||
conversation?.endpoint,
|
||||
conversation?.iconURL,
|
||||
conversation?.model,
|
||||
message?.model,
|
||||
message?.iconURL,
|
||||
message?.isCreatedByUser,
|
||||
],
|
||||
);
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -62,12 +83,7 @@ export default function Message(props: TMessageProps) {
|
|||
<div>
|
||||
<div className="pt-0.5">
|
||||
<div className="shadow-stroke flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
||||
<Icon
|
||||
message={message}
|
||||
conversation={conversation}
|
||||
assistant={assistant}
|
||||
agent={agent}
|
||||
/>
|
||||
<MessageIcon iconData={iconData} assistant={assistant} agent={agent} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export default function MessagesView({
|
|||
</div>
|
||||
) : (
|
||||
<>
|
||||
{Header && Header}
|
||||
{Header != null && Header}
|
||||
<div ref={screenshotTargetRef}>
|
||||
<MultiMessage
|
||||
key={conversationId} // avoid internal state mixture
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useAuthContext, useLocalize } from '~/hooks';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import MinimalHoverButtons from '~/components/Chat/Messages/MinimalHoverButtons';
|
||||
import Icon from '~/components/Chat/Messages/MessageIcon';
|
||||
|
|
@ -15,6 +17,17 @@ export default function Message({ message }: Pick<TMessageProps, 'message'>) {
|
|||
const { user } = useAuthContext();
|
||||
const localize = useLocalize();
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: message?.endpoint,
|
||||
model: message?.model,
|
||||
iconURL: message?.iconURL ?? '',
|
||||
isCreatedByUser: message?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
[message?.model, message?.iconURL, message?.endpoint, message?.isCreatedByUser],
|
||||
);
|
||||
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -27,7 +40,7 @@ export default function Message({ message }: Pick<TMessageProps, 'message'>) {
|
|||
? (user?.name ?? '') || (user?.username ?? '')
|
||||
: localize('com_user_message');
|
||||
} else {
|
||||
messageLabel = message.sender || '';
|
||||
messageLabel = message.sender ?? '';
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -39,7 +52,7 @@ export default function Message({ message }: Pick<TMessageProps, 'message'>) {
|
|||
<div>
|
||||
<div className="pt-0.5">
|
||||
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
||||
<Icon message={message} />
|
||||
<Icon iconData={iconData} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import MessageContent from '~/components/Chat/Messages/Content/MessageContent';
|
|||
import PlaceholderRow from '~/components/Chat/Messages/ui/PlaceholderRow';
|
||||
import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch';
|
||||
import HoverButtons from '~/components/Chat/Messages/HoverButtons';
|
||||
import Icon from '~/components/Chat/Messages/MessageIcon';
|
||||
import MessageIcon from '~/components/Chat/Messages/MessageIcon';
|
||||
import { Plugin } from '~/components/Messages/Content';
|
||||
import SubRow from '~/components/Chat/Messages/SubRow';
|
||||
import { MessageContext } from '~/Providers';
|
||||
|
|
@ -66,6 +66,27 @@ const MessageRender = memo(
|
|||
[hasNoChildren, msg?.depth, latestMessage?.depth],
|
||||
);
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: conversation?.endpoint,
|
||||
model: conversation?.model ?? msg?.model,
|
||||
iconURL: conversation?.iconURL ?? msg?.iconURL ?? '',
|
||||
modelLabel: conversation?.chatGptLabel ?? conversation?.modelLabel,
|
||||
isCreatedByUser: msg?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
[
|
||||
conversation?.chatGptLabel,
|
||||
conversation?.modelLabel,
|
||||
conversation?.endpoint,
|
||||
conversation?.iconURL,
|
||||
conversation?.model,
|
||||
msg?.model,
|
||||
msg?.iconURL,
|
||||
msg?.isCreatedByUser,
|
||||
],
|
||||
);
|
||||
|
||||
if (!msg) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -125,7 +146,7 @@ const MessageRender = memo(
|
|||
<div>
|
||||
<div className="pt-0.5">
|
||||
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
||||
<Icon message={msg} conversation={conversation} assistant={assistant} />
|
||||
<MessageIcon iconData={iconData} assistant={assistant} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue