fix: prevent stale selectChat async results from clobbering new session task lane
Race condition: on page remount, selectChat(oldId) loads session data async. If the user clicks New Chat before the API returns, the old session's pending_task_lane was being applied to the new session's state, showing stale tasks and blocking new ones from appearing. Fix: currentChatRef tracks the most recently requested chat ID synchronously. All chat-creation paths (selectChat, handleNewChat, handleResumeNew) update it immediately. After each await in selectChat, bail if the ref no longer matches. Also documents the pattern as Lesson 106 in CLAUDE.md for future reference. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -81,6 +81,9 @@ export default function AssistantChatPage() {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const dragCounterRef = useRef(0)
|
||||
const prefillHandledRef = useRef(false)
|
||||
// Tracks the most recently requested active chat ID so in-flight selectChat
|
||||
// calls that complete after the user switches chats don't clobber new state.
|
||||
const currentChatRef = useRef<string | null>(activeChatId)
|
||||
|
||||
// Persist active chat ID to sessionStorage
|
||||
useEffect(() => {
|
||||
@@ -214,6 +217,7 @@ export default function AssistantChatPage() {
|
||||
}
|
||||
|
||||
const selectChat = useCallback(async (chatId: string) => {
|
||||
currentChatRef.current = chatId
|
||||
setActiveChatId(chatId)
|
||||
// Clear TaskLane when switching chats — will restore from backend if available
|
||||
setShowTaskLane(false)
|
||||
@@ -221,6 +225,10 @@ export default function AssistantChatPage() {
|
||||
setActiveActions([])
|
||||
try {
|
||||
const detail = await aiSessionsApi.getSession(chatId)
|
||||
// Guard: if the user switched to a different chat while this API call was
|
||||
// in flight (e.g. clicked "New Chat"), discard stale results so we don't
|
||||
// clobber the new session's task lane state.
|
||||
if (currentChatRef.current !== chatId) return
|
||||
setMessages(
|
||||
(detail.conversation_messages || []).map(m => ({
|
||||
role: m.role as 'user' | 'assistant',
|
||||
@@ -264,6 +272,7 @@ export default function AssistantChatPage() {
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
}
|
||||
currentChatRef.current = session.session_id
|
||||
setChats(prev => [chatItem, ...prev])
|
||||
setActiveChatId(session.session_id)
|
||||
setMessages([])
|
||||
@@ -430,6 +439,7 @@ export default function AssistantChatPage() {
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
}
|
||||
currentChatRef.current = session.session_id
|
||||
setChats(prev => [chatItem, ...prev])
|
||||
setActiveChatId(session.session_id)
|
||||
setMessages([{ role: 'user', content: resumePrompt }])
|
||||
|
||||
Reference in New Issue
Block a user