fix(#338): Fix useChat stale messages with functional state updates
- Add messagesRef to track current messages and prevent stale closures - Use functional updates for all setMessages calls - Remove messages from sendMessage dependency array - Add comprehensive tests verifying rapid sends don't lose messages Refs #338 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,10 @@ export function useChat(options: UseChatOptions = {}): UseChatReturn {
|
||||
const projectIdRef = useRef<string | null>(projectId ?? null);
|
||||
projectIdRef.current = projectId ?? null;
|
||||
|
||||
// Track messages in ref to prevent stale closures during rapid sends
|
||||
const messagesRef = useRef<Message[]>(messages);
|
||||
messagesRef.current = messages;
|
||||
|
||||
/**
|
||||
* Convert our Message format to API ChatMessage format
|
||||
*/
|
||||
@@ -156,15 +160,19 @@ export function useChat(options: UseChatOptions = {}): UseChatReturn {
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Add user message immediately
|
||||
setMessages((prev) => [...prev, userMessage]);
|
||||
// Add user message immediately using functional update
|
||||
setMessages((prev) => {
|
||||
const updated = [...prev, userMessage];
|
||||
messagesRef.current = updated;
|
||||
return updated;
|
||||
});
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// Prepare API request
|
||||
const updatedMessages = [...messages, userMessage];
|
||||
const apiMessages = convertToApiMessages(updatedMessages);
|
||||
// Prepare API request - use ref to get current messages (prevents stale closure)
|
||||
const currentMessages = messagesRef.current;
|
||||
const apiMessages = convertToApiMessages(currentMessages);
|
||||
|
||||
const request = {
|
||||
model,
|
||||
@@ -189,9 +197,13 @@ export function useChat(options: UseChatOptions = {}): UseChatReturn {
|
||||
totalTokens: (response.promptEvalCount ?? 0) + (response.evalCount ?? 0),
|
||||
};
|
||||
|
||||
// Add assistant message
|
||||
const finalMessages = [...updatedMessages, assistantMessage];
|
||||
setMessages(finalMessages);
|
||||
// Add assistant message using functional update
|
||||
let finalMessages: Message[] = [];
|
||||
setMessages((prev) => {
|
||||
finalMessages = [...prev, assistantMessage];
|
||||
messagesRef.current = finalMessages;
|
||||
return finalMessages;
|
||||
});
|
||||
|
||||
// Generate title from first user message if this is a new conversation
|
||||
const isFirstMessage =
|
||||
@@ -220,7 +232,6 @@ export function useChat(options: UseChatOptions = {}): UseChatReturn {
|
||||
}
|
||||
},
|
||||
[
|
||||
messages,
|
||||
isLoading,
|
||||
conversationId,
|
||||
conversationTitle,
|
||||
|
||||
Reference in New Issue
Block a user