فهرست منبع

🐛fix: Fix React hooks order violation causing page crash on API errors

- 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.
Apple\Apple 11 ماه پیش
والد
کامیت
f9c8a802ef
2فایلهای تغییر یافته به همراه12 افزوده شده و 15 حذف شده
  1. 9 9
      web/src/components/playground/MessageContent.js
  2. 3 6
      web/src/components/playground/ThinkingContent.js

+ 9 - 9
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 &&

+ 3 - 6
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;
   }