fix: convo component re-render every single token yield

This commit is contained in:
Louis 2024-11-28 12:42:27 +07:00
parent cd1a2746ce
commit 2e35b2f20d
No known key found for this signature in database
GPG Key ID: 44FA9F4D33C37DE2
2 changed files with 90 additions and 29 deletions

View File

@ -1,6 +1,6 @@
import { memo } from 'react' import { memo, useEffect, useState } from 'react'
import { MessageStatus } from '@janhq/core' import { MessageStatus, ThreadMessage } from '@janhq/core'
import { useAtomValue } from 'jotai' import { useAtomValue } from 'jotai'
@ -17,19 +17,51 @@ import EmptyThread from './EmptyThread'
import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom'
const ChatBody = () => { const ChatConfigurator = memo(() => {
const messages = useAtomValue(getCurrentChatMessagesAtom) const messages = useAtomValue(getCurrentChatMessagesAtom)
const [current, setCurrent] = useState<ThreadMessage[]>([])
const isMessagesIdentificial = (
arr1: ThreadMessage[],
arr2: ThreadMessage[]
): boolean => {
if (arr1.length !== arr2.length) return false
return arr1.every((item, index) => item.id === arr2[index].id)
}
useEffect(() => {
if (
messages.length !== current.length ||
!isMessagesIdentificial(messages, current)
) {
setCurrent(messages)
}
}, [messages, current])
const loadModelError = useAtomValue(loadModelErrorAtom) const loadModelError = useAtomValue(loadModelErrorAtom)
if (!messages.length) return <EmptyThread /> if (!messages.length) return <EmptyThread />
return (
<div className="flex h-full w-full flex-col">
<ChatBody loadModelError={loadModelError} messages={current} />
</div>
)
})
const ChatBody = memo(
({
messages,
loadModelError,
}: {
messages: ThreadMessage[]
loadModelError?: string
}) => {
return ( return (
<ListContainer> <ListContainer>
{messages.map((message, index) => ( {messages.map((message, index) => (
<div key={message.id}> <div key={message.id}>
{message.status !== MessageStatus.Error && {message.status !== MessageStatus.Error && (
message.content.length > 0 && (
<ChatItem {...message} key={message.id} /> <ChatItem {...message} key={message.id} />
)} )}
@ -41,9 +73,11 @@ const ChatBody = () => {
)} )}
</div> </div>
))} ))}
{loadModelError && <LoadModelError />} {loadModelError && <LoadModelError />}
</ListContainer> </ListContainer>
) )
} }
)
export default memo(ChatBody) export default memo(ChatConfigurator)

View File

@ -1,15 +1,42 @@
import React, { forwardRef } from 'react' import React, { forwardRef, useEffect, useState } from 'react'
import { ThreadMessage } from '@janhq/core' import {
events,
MessageEvent,
MessageStatus,
ThreadContent,
ThreadMessage,
} from '@janhq/core'
import SimpleTextMessage from '../SimpleTextMessage' import SimpleTextMessage from '../SimpleTextMessage'
type Ref = HTMLDivElement type Ref = HTMLDivElement
const ChatItem = forwardRef<Ref, ThreadMessage>((message, ref) => ( const ChatItem = forwardRef<Ref, ThreadMessage>((message, ref) => {
const [content, setContent] = useState<ThreadContent[]>(message.content)
const [status, setStatus] = useState<MessageStatus>(message.status)
function onMessageUpdate(data: ThreadMessage) {
if (data.id === message.id) {
setContent(data.content)
if (data.status !== status) setStatus(data.status)
}
}
useEffect(() => {
if (message.status === MessageStatus.Pending)
events.on(MessageEvent.OnMessageUpdate, onMessageUpdate)
return () => {
events.off(MessageEvent.OnMessageUpdate, onMessageUpdate)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<div ref={ref} className="relative"> <div ref={ref} className="relative">
<SimpleTextMessage {...message} /> <SimpleTextMessage {...message} content={content} status={status} />
</div> </div>
)) )
})
export default ChatItem export default ChatItem