refactor: Optimize message handling and event management

- Introduce messagesRef to track messages without causing re-renders
- Simplify event listener management with more concise useEffect hooks
- Improve auto-rename topic logic with current messages reference
- Remove commented-out code and unused event listeners
- Enhance type safety and reduce dependency complexity
This commit is contained in:
kangfenmao 2025-03-08 20:45:28 +08:00
parent 49d29d78da
commit 3312befe11
2 changed files with 46 additions and 30 deletions

View File

@ -50,6 +50,12 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic })
const [hasMore, setHasMore] = useState(false) const [hasMore, setHasMore] = useState(false)
const [isLoadingMore, setIsLoadingMore] = useState(false) const [isLoadingMore, setIsLoadingMore] = useState(false)
const messagesRef = useRef<Message[]>([])
useEffect(() => {
messagesRef.current = messages
}, [messages])
useEffect(() => { useEffect(() => {
const reversedMessages = [...messages].reverse() const reversedMessages = [...messages].reverse()
const newDisplayMessages = reversedMessages.slice(0, displayCount) const newDisplayMessages = reversedMessages.slice(0, displayCount)
@ -85,15 +91,8 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic })
setTimeout(() => containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'auto' }), 50) setTimeout(() => containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'auto' }), 50)
}, []) }, [])
// const onAppendMessageMemo = useCallback( const autoRenameTopic = useCallback(async () => {
// async (message: Message) => { const messages = messagesRef.current
// const newMessages = [...messages, message]
// await dispatch(updateMessages(topic, newMessages))
// },
// [topic, dispatch, messages]
// )
const autoRenameTopicMemo = useCallback(async () => {
const _topic = getTopic(assistant, topic.id) const _topic = getTopic(assistant, topic.id)
if (!enableTopicNaming) { if (!enableTopicNaming) {
@ -114,18 +113,16 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic })
updateTopic(data) updateTopic(data)
} }
} }
}, [assistant, enableTopicNaming, messages, setActiveTopic, topic.id, updateTopic, t]) // eslint-disable-next-line react-hooks/exhaustive-deps
}, [assistant, topic.id, enableTopicNaming, t, setActiveTopic])
useEffect(() => { useEffect(() => {
const messages = messagesRef.current
const unsubscribes = [ const unsubscribes = [
// EventEmitter.on(EVENT_NAMES.APPEND_MESSAGE, onAppendMessageMemo),
// EventEmitter.on(EVENT_NAMES.RECEIVE_MESSAGE, () => {
// setTimeout(() => EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME), 100)
// }),
EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, () => { EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, () => {
scrollToBottom() scrollToBottom()
}), }),
EventEmitter.on(EVENT_NAMES.AI_AUTO_RENAME, autoRenameTopicMemo),
EventEmitter.on(EVENT_NAMES.CLEAR_MESSAGES, async (data: Topic) => { EventEmitter.on(EVENT_NAMES.CLEAR_MESSAGES, async (data: Topic) => {
const defaultTopic = getDefaultTopic(assistant.id) const defaultTopic = getDefaultTopic(assistant.id)
@ -172,12 +169,13 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic })
}) })
] ]
return () => { return () => unsubscribes.forEach((unsub) => unsub())
for (const unsub of unsubscribes) { }, [assistant, dispatch, handleDeleteMessage, scrollToBottom, topic, updateTopic])
unsub()
} useEffect(() => {
} const unsubscribes = [EventEmitter.on(EVENT_NAMES.AI_AUTO_RENAME, autoRenameTopic)]
}, [assistant, autoRenameTopicMemo, dispatch, messages, handleDeleteMessage, scrollToBottom, topic, updateTopic]) return () => unsubscribes.forEach((unsub) => unsub())
}, [autoRenameTopic])
useEffect(() => { useEffect(() => {
runAsyncFunction(async () => { runAsyncFunction(async () => {

View File

@ -172,9 +172,13 @@ export const {
const handleResponseMessageUpdate = (message, topicId, dispatch, getState) => { const handleResponseMessageUpdate = (message, topicId, dispatch, getState) => {
dispatch(setStreamMessage({ topicId, message })) dispatch(setStreamMessage({ topicId, message }))
// When message is complete, commit to messages and sync with DB // When message is complete, commit to messages and sync with DB
if (message.status !== 'pending') { if (message.status !== 'pending') {
if (message.status === 'success') {
EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME) EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME)
}
dispatch(commitStreamMessage({ topicId, messageId: message.id })) dispatch(commitStreamMessage({ topicId, messageId: message.id }))
const state = getState() const state = getState()
@ -225,7 +229,7 @@ export const sendMessage =
// 使用用户消息 // 使用用户消息
let userMessage: Message let userMessage: Message
if (isResend) { if (isResend) {
userMessage = options.resendUserMessage userMessage = options.resendUserMessage!
} else { } else {
// 创建新的用户消息 // 创建新的用户消息
userMessage = getUserMessage({ assistant, topic, type: 'text', content }) userMessage = getUserMessage({ assistant, topic, type: 'text', content })
@ -233,9 +237,11 @@ export const sendMessage =
if (options?.files) { if (options?.files) {
userMessage.files = options.files userMessage.files = options.files
} }
if (options?.knowledgeBaseIds) { if (options?.knowledgeBaseIds) {
userMessage.knowledgeBaseIds = options.knowledgeBaseIds userMessage.knowledgeBaseIds = options.knowledgeBaseIds
} }
if (options?.mentionModels) { if (options?.mentionModels) {
userMessage.mentions = options.mentionModels userMessage.mentions = options.mentionModels
} }
@ -245,6 +251,7 @@ export const sendMessage =
if (!isResend) { if (!isResend) {
dispatch(addMessage({ topicId: topic.id, messages: userMessage })) dispatch(addMessage({ topicId: topic.id, messages: userMessage }))
} }
EventEmitter.emit(EVENT_NAMES.SEND_MESSAGE) EventEmitter.emit(EVENT_NAMES.SEND_MESSAGE)
// 处理助手消息 // 处理助手消息
@ -284,8 +291,12 @@ export const sendMessage =
// Use topic queue to handle request // Use topic queue to handle request
const queue = getTopicQueue(topic.id) const queue = getTopicQueue(topic.id)
// let assistantMessage: Message | undefined // let assistantMessage: Message | undefined
!isResend && dispatch(addMessage({ topicId: topic.id, messages: assistantMessages })) if (!isResend) {
dispatch(addMessage({ topicId: topic.id, messages: assistantMessages }))
}
for (const assistantMessage of assistantMessages) { for (const assistantMessage of assistantMessages) {
// console.log('assistantMessage', assistantMessage) // console.log('assistantMessage', assistantMessage)
@ -295,9 +306,11 @@ export const sendMessage =
// Sync user message with database // Sync user message with database
const state = getState() const state = getState()
const currentTopicMessages = state.messages.messagesByTopic[topic.id] const currentTopicMessages = state.messages.messagesByTopic[topic.id]
if (currentTopicMessages) { if (currentTopicMessages) {
await syncMessagesWithDB(topic.id, currentTopicMessages) await syncMessagesWithDB(topic.id, currentTopicMessages)
} }
queue.add(async () => { queue.add(async () => {
try { try {
const state = getState() const state = getState()
@ -340,7 +353,7 @@ export const sendMessage =
throttledDispatch({ ...assistantMessage, ...updatedMsg }, topic.id, dispatch, getState) throttledDispatch({ ...assistantMessage, ...updatedMsg }, topic.id, dispatch, getState)
} }
}) })
} catch (error) { } catch (error: any) {
console.error('Error in chat completion:', error) console.error('Error in chat completion:', error)
dispatch( dispatch(
updateMessage({ updateMessage({
@ -354,7 +367,7 @@ export const sendMessage =
} }
}) })
} }
} catch (error) { } catch (error: any) {
console.error('Error in sendMessage:', error) console.error('Error in sendMessage:', error)
dispatch(setError(error.message)) dispatch(setError(error.message))
} finally { } finally {
@ -409,7 +422,7 @@ export const resendMessage =
resendAssistantMessage: message resendAssistantMessage: message
}) })
) )
} catch (error) { } catch (error: any) {
console.error('Error in resendMessage:', error) console.error('Error in resendMessage:', error)
dispatch(setError(error.message)) dispatch(setError(error.message))
} finally { } finally {
@ -477,7 +490,10 @@ export const selectTopicMessages = createSelector(
[(state: RootState) => state.messages, (_, topicId: string) => topicId], [(state: RootState) => state.messages, (_, topicId: string) => topicId],
(messagesState, topicId) => { (messagesState, topicId) => {
const topicMessages = messagesState.messagesByTopic[topicId] const topicMessages = messagesState.messagesByTopic[topicId]
if (!topicMessages) return []
if (!topicMessages) {
return []
}
return [...topicMessages].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()) return [...topicMessages].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
} }
@ -503,7 +519,9 @@ export const selectError = (state: RootState): string | null => {
return messagesState?.error || null return messagesState?.error || null
} }
export const selectStreamMessage = (state: RootState, topicId: string, messageId: string): Message | null => export const selectStreamMessage = (state: RootState, topicId: string, messageId: string): Message | null => {
state.messages.streamMessagesByTopic[topicId]?.[messageId] || null const messagesState = state.messages as MessagesState
return messagesState.streamMessagesByTopic[topicId]?.[messageId] || null
}
export default messagesSlice.reducer export default messagesSlice.reducer