From f9c8a802ef1ca6d2abec6d22d986e8420506a394 Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Mon, 2 Jun 2025 21:26:56 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9Bfix:=20Fix=20React=20hooks=20order?= =?UTF-8?q?=20violation=20causing=20page=20crash=20on=20API=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move useEffect hooks before conditional returns in MessageContent and ThinkingContent - Ensure hooks are called in the same order every render - Fix "Rendered fewer hooks than expected" error when API returns non-200 status - Follow React hooks rules: only call hooks at the top level This prevents the entire page from crashing when API requests fail. --- .../components/playground/MessageContent.js | 18 +++++++++--------- .../components/playground/ThinkingContent.js | 9 +++------ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/web/src/components/playground/MessageContent.js b/web/src/components/playground/MessageContent.js index 2e8eb548..5988c844 100644 --- a/web/src/components/playground/MessageContent.js +++ b/web/src/components/playground/MessageContent.js @@ -28,6 +28,15 @@ const MessageContent = ({ const previousContentLengthRef = useRef(0); const lastContentRef = useRef(''); + const isThinkingStatus = message.status === 'loading' || message.status === 'incomplete'; + + useEffect(() => { + if (!isThinkingStatus) { + previousContentLengthRef.current = 0; + lastContentRef.current = ''; + } + }, [isThinkingStatus]); + if (message.status === 'error') { let errorText; @@ -51,7 +60,6 @@ const MessageContent = ({ ); } - const isThinkingStatus = message.status === 'loading' || message.status === 'incomplete'; let currentExtractedThinkingContent = null; let currentDisplayableFinalContent = ""; let thinkingSource = null; @@ -130,14 +138,6 @@ const MessageContent = ({ const finalExtractedThinkingContent = currentExtractedThinkingContent; const finalDisplayableFinalContent = currentDisplayableFinalContent; - // 流式状态结束时重置 - useEffect(() => { - if (!isThinkingStatus) { - previousContentLengthRef.current = 0; - lastContentRef.current = ''; - } - }, [isThinkingStatus]); - if (message.role === 'assistant' && isThinkingStatus && !finalExtractedThinkingContent && diff --git a/web/src/components/playground/ThinkingContent.js b/web/src/components/playground/ThinkingContent.js index 57b4f86d..d5210507 100644 --- a/web/src/components/playground/ThinkingContent.js +++ b/web/src/components/playground/ThinkingContent.js @@ -15,25 +15,23 @@ const ThinkingContent = ({ const scrollRef = useRef(null); const lastContentRef = useRef(''); - if (!finalExtractedThinkingContent) return null; - const isThinkingStatus = message.status === 'loading' || message.status === 'incomplete'; const headerText = (isThinkingStatus && !message.isThinkingComplete) ? t('思考中...') : t('思考过程'); useEffect(() => { - if (scrollRef.current) { + if (scrollRef.current && finalExtractedThinkingContent && message.isReasoningExpanded) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [finalExtractedThinkingContent, message.isReasoningExpanded]); - // 流式状态结束时重置 useEffect(() => { if (!isThinkingStatus) { lastContentRef.current = ''; } }, [isThinkingStatus]); - // 获取上一次的内容长度 + if (!finalExtractedThinkingContent) return null; + let prevLength = 0; if (isThinkingStatus && lastContentRef.current) { if (finalExtractedThinkingContent.startsWith(lastContentRef.current)) { @@ -41,7 +39,6 @@ const ThinkingContent = ({ } } - // 更新最后内容的引用 if (isThinkingStatus) { lastContentRef.current = finalExtractedThinkingContent; }