# ReviewRunner 組件詳細說明文檔 ## 📋 組件概覽 `ReviewRunner` 是複習系統的**核心容器組件**,負責協調 7 種不同的複習模式、管理測驗生命週期、處理答題邏輯,以及控制測驗間的導航流程。 **文件位置**: `/frontend/components/review/ReviewRunner.tsx` **組件類型**: 容器組件 (Container Component) **職責範圍**: 業務邏輯 + 狀態管理 + 組件編排 ## 🏗️ 組件架構設計 ### 架構模式:容器-展示分離 ``` ReviewRunner (容器組件) ├── 狀態管理 (4個 Zustand Store) ├── 業務邏輯 (答題處理、導航控制) ├── 組件編排 (動態渲染7種測驗) └── 智能導航 (SmartNavigationController) ``` **設計哲學**: - **容器組件**: 處理邏輯和狀態,不涉及UI細節 - **展示組件**: 純UI渲染,接收 props 和回調 - **關注點分離**: 業務邏輯與UI邏輯完全分離 ## 📊 依賴關係分析 ### Store 依賴關係 ```typescript // 4個 Zustand Store 的使用 useReviewSessionStore // 當前卡片、錯誤狀態 ├── currentCard // 當前複習的詞卡 ├── error // 會話錯誤狀態 useTestQueueStore // 測驗佇列管理 ├── currentMode // 當前測驗模式 ├── testItems // 測驗項目陣列 ├── currentTestIndex // 當前測驗索引 ├── markTestCompleted // 標記測驗完成 ├── goToNextTest // 切換下一個測驗 └── skipCurrentTest // 跳過當前測驗 useTestResultStore // 測驗結果記錄 ├── score // 當前分數統計 ├── updateScore // 更新分數 └── recordTestResult // 記錄到後端 useReviewUIStore // UI 狀態管理 ├── openReportModal // 開啟錯誤報告彈窗 └── openImageModal // 開啟圖片放大彈窗 ``` **依賴流程圖**: ``` TestQueueStore (提供測驗項目) ↓ ReviewRunner (協調各Store + 渲染組件) ↓ TestComponent (處理用戶交互) ↓ (答案回調) ReviewRunner.handleAnswer() ↓ (更新狀態) TestResultStore + TestQueueStore + 後端API ``` ## 🔄 核心方法詳解 ### 1. handleAnswer - 答題處理核心邏輯 ```typescript const handleAnswer = useCallback(async (answer: string, confidenceLevel?: number) => { // 防護性檢查:避免重複提交或無效狀態下提交 if (!currentCard || hasAnswered || isProcessingAnswer) return setIsProcessingAnswer(true) // 設置處理中狀態 try { // 第1步:答案驗證 const isCorrect = checkAnswer(answer, currentCard, currentMode) // 第2步:本地狀態立即更新 updateScore(isCorrect) // 第3步:異步同步到後端 const success = await recordTestResult({ flashcardId: currentCard.id, testType: currentMode, isCorrect, userAnswer: answer, confidenceLevel, responseTimeMs: 2000 // 可以改為實際測量值 }) // 第4步:更新測驗佇列狀態 if (success) { markTestCompleted(currentTestIndex) setHasAnswered(true) // 啟用"繼續"按鈕 // 第5步:答錯處理邏輯 (TODO: 未完全實現) if (!isCorrect && currentMode !== 'flip-memory') { console.log('答錯,將重新排入隊列') // TODO: 實現優先級重排邏輯 } } } catch (error) { console.error('答題處理失敗:', error) // 錯誤處理:可以顯示錯誤提示或重試機制 } finally { setIsProcessingAnswer(false) // 解除處理中狀態 } }, [currentCard, hasAnswered, isProcessingAnswer, currentMode, updateScore, recordTestResult, markTestCompleted, currentTestIndex]) ``` **設計特色**: - **防護性檢查**: 避免重複提交和無效狀態 - **樂觀更新**: 本地狀態立即更新,異步同步後端 - **錯誤容錯**: 完整的 try-catch 錯誤處理 - **狀態控制**: `isProcessingAnswer` 防止按鈕重複點擊 ### 2. checkAnswer - 答案驗證邏輯 ```typescript const checkAnswer = (answer: string, card: any, mode: string): boolean => { switch (mode) { case 'flip-memory': return true // 翻卡記憶沒有對錯,只有信心等級 case 'vocab-choice': case 'vocab-listening': return answer === card.word // 精確匹配詞彙 case 'sentence-fill': return answer.toLowerCase().trim() === card.word.toLowerCase() // 忽略大小寫 case 'sentence-reorder': case 'sentence-listening': return answer.toLowerCase().trim() === card.example.toLowerCase().trim() // 句子匹配 case 'sentence-speaking': return true // 口說測驗通常算正確 (語音識別待實現) default: return false } } ``` **演算法特色**: - **模式特化**: 每種測驗類型有專門的驗證邏輯 - **容錯設計**: 忽略大小寫和空格 - **擴展性**: 易於添加新的測驗類型驗證 ### 3. generateOptions - 選項生成演算法 ```typescript const generateOptions = (card: any, mode: string): string[] => { switch (mode) { case 'vocab-choice': case 'vocab-listening': // 詞彙選擇:生成3個干擾項 + 1個正確答案 return [card.word, '其他選項1', '其他選項2', '其他選項3'] .sort(() => Math.random() - 0.5) // 隨機排序 case 'sentence-listening': // 句子聽力:生成3個例句干擾項 + 1個正確例句 return [ card.example, '其他例句選項1', '其他例句選項2', '其他例句選項3' ].sort(() => Math.random() - 0.5) default: return [] // 其他模式不需要選項 } } ``` **改進空間** (目前為簡化實現): - 真實干擾項應基於詞性、CEFR等級、語義相似性生成 - 需要避免過於簡單或過於困難的干擾項 - 可考慮用戶歷史錯誤答案作為干擾項 ## 🎛️ 狀態管理流程 ### 本地狀態設計 ```typescript interface ReviewRunnerState { hasAnswered: boolean // 是否已答題(控制導航按鈕顯示) isProcessingAnswer: boolean // 是否正在處理答案(防重複提交) } ``` **狀態轉換流程**: ``` 初始狀態: hasAnswered=false, isProcessingAnswer=false ↓ (用戶答題) 處理中: hasAnswered=false, isProcessingAnswer=true ↓ (處理完成) 已答題: hasAnswered=true, isProcessingAnswer=false ↓ (點擊繼續/跳過) 重置狀態: hasAnswered=false, isProcessingAnswer=false (下一題) ``` ### 生命週期管理 ```typescript // 測驗切換時自動重置狀態 useEffect(() => { setHasAnswered(false) setIsProcessingAnswer(false) }, [currentTestIndex, currentMode]) ``` **重置觸發條件**: - `currentTestIndex` 改變:切換到新測驗 - `currentMode` 改變:切換測驗類型 - 用戶主動跳過或繼續 ## 🎮 動態組件渲染系統 ### 組件映射機制 ```typescript // 測驗組件映射表 const TEST_COMPONENTS = { 'flip-memory': FlipMemoryTest, 'vocab-choice': VocabChoiceTest, 'sentence-fill': SentenceFillTest, 'sentence-reorder': SentenceReorderTest, 'vocab-listening': VocabListeningTest, 'sentence-listening': SentenceListeningTest, 'sentence-speaking': SentenceSpeakingTest } as const ``` **動態渲染邏輯**: ```typescript const renderTestContent = () => { // 基於 currentMode 動態選擇組件 switch (currentMode) { case 'flip-memory': return ( handleAnswer('', level)} // 特殊處理 disabled={isProcessingAnswer} // 處理中禁用 /> ) // ... 其他模式 } } ``` **設計優勢**: - **統一介面**: 所有測驗組件使用相同的 `commonProps` - **特化處理**: 各測驗的特殊需求通過額外 props 處理 - **類型安全**: TypeScript 確保正確的 props 傳遞 ## 🔀 雙重渲染模式 ### 模式1:真實數據模式 (Production) ```typescript // 使用真實的 currentCard 數據 if (currentCard) { const cardData = { id: currentCard.id, word: currentCard.word, definition: currentCard.definition, // ... 完整詞卡數據 } return renderTestContent() // 渲染真實測驗 } ``` ### 模式2:模擬數據模式 (Development/Testing) ```typescript // 當沒有真實數據時,使用 mockFlashcards if (!currentCard && testItems.length > 0) { const currentTest = testItems[currentTestIndex] const mockCard = mockFlashcards.find(card => card.id === currentTest.cardId) if (mockCard) { return renderTestContentWithMockData(mockCard, currentTest.testType, mockOptions) } } ``` **雙重模式的價值**: - **開發便利**: 無需後端數據即可測試複習功能 - **錯誤容錯**: 真實數據載入失敗時的降級方案 - **獨立測試**: 前端邏輯可獨立於後端進行測試 ## 🎯 導航控制邏輯 ### SmartNavigationController 整合 ```typescript ``` **導航邏輯流程**: ``` 未答題階段: 顯示"跳過"按鈕 ↓ (用戶答題) 已答題階段: 顯示"繼續"按鈕 ↓ (用戶點擊繼續) 狀態重置: 準備下一題 ``` ### 跳過和繼續處理 ```typescript // 跳過邏輯 const handleSkip = useCallback(() => { if (hasAnswered) return // 已答題後不能跳過 skipCurrentTest() // 更新 TestQueue Store // 重置本地狀態,準備下一題 setHasAnswered(false) setIsProcessingAnswer(false) }, [hasAnswered, skipCurrentTest]) // 繼續邏輯 const handleContinue = useCallback(() => { if (!hasAnswered) return // 未答題不能繼續 goToNextTest() // 切換到下一個測驗 // 重置本地狀態,準備下一題 setHasAnswered(false) setIsProcessingAnswer(false) }, [hasAnswered, goToNextTest]) ``` ## 📈 進度追蹤系統 ### ProgressBar 整合 ```typescript {/* 進度條顯示邏輯 */} {testItems.length > 0 && (
item.isSkipped).length} // 跳過數量 />
)} ``` **進度計算邏輯**: - **完成進度**: `currentTestIndex / testItems.length * 100` - **正確率**: `score.correct / score.total * 100` - **跳過統計**: 實時統計跳過的測驗數量 ## 🧮 測驗組件 Props 設計 ### 統一的 commonProps ```typescript const commonProps = { cardData, // 標準化的卡片數據 onAnswer: handleAnswer, // 統一的答題回調 onReportError: () => openReportModal(currentCard) // 錯誤報告回調 } ``` ### 特化的額外 Props ```typescript // 翻卡記憶:信心度提交 handleAnswer('', level)} // 信心度→答題 disabled={isProcessingAnswer} /> // 選擇題類型:選項陣列 // 圖片相關測驗:圖片處理 ``` ## 🎨 用戶體驗設計 ### 載入和錯誤狀態處理 ```typescript // 錯誤狀態顯示 if (error) { return (

發生錯誤

{error}

) } // 載入狀態顯示 if (!currentCard) { return (
載入測驗中...
) } ``` **UX 設計原則**: - **即時反饋**: 用戶操作立即得到視覺回饋 - **狀態明確**: 清晰區分載入、錯誤、正常狀態 - **防誤操作**: 處理中狀態禁用所有交互 ### 視覺層次和佈局 ```typescript return (
{/* 第1層:進度追蹤 */}
{/* 第2層:測驗內容 (主要區域) */}
{renderTestContent()}
{/* 第3層:導航控制 */}
) ``` **佈局設計**: - **視覺層次**: 進度→內容→導航,符合用戶視線流 - **間距統一**: 使用 `mb-6` 保持一致間距 - **分隔線**: `border-t` 明確區分導航區域 ## ⚡ 性能優化策略 ### useCallback 優化 ```typescript // 依賴項精確控制,避免不必要的重新創建 const handleAnswer = useCallback(async (answer: string, confidenceLevel?: number) => { // 答題邏輯 }, [currentCard, hasAnswered, isProcessingAnswer, currentMode, updateScore, recordTestResult, markTestCompleted, currentTestIndex]) // 依賴項最小化 const handleSkip = useCallback(() => { // 跳過邏輯 }, [hasAnswered, skipCurrentTest]) ``` **優化原則**: - **依賴項精確**: 只包含實際使用的變數 - **穩定引用**: 避免子組件不必要重渲染 - **記憶化**: 複雜函數使用 useCallback ### 條件渲染優化 ```typescript // 避免不必要的組件創建 {testItems.length > 0 && ( // 條件:有測驗項目 // 才創建進度條 )} // 提前返回,減少後續計算 if (error) return if (!currentCard) return ``` ## 🔧 技術債務和改進點 ### 當前技術債務 1. **generateOptions 實現簡化**: ```typescript // 當前實現:硬編碼假選項 return [card.word, '其他選項1', '其他選項2', '其他選項3'] // 理想實現:智能干擾項生成 const generateIntelligentDistractors = (correctWord: string, allCards: Card[]): string[] => { const samePOS = allCards.filter(c => c.partOfSpeech === correctWord.partOfSpeech) const similarCEFR = allCards.filter(c => c.cefr === correctWord.cefr) const semanticallySimilar = findSemanticallySimilar(correctWord, allCards) return intelligentlySelect(samePOS, similarCEFR, semanticallySimilar, 3) } ``` 2. **答錯重排邏輯未完整實現**: ```typescript // TODO 部分:需要實現完整的優先級重排 if (!isCorrect && currentMode !== 'flip-memory') { // 當前:只有 console.log console.log('答錯,將重新排入隊列') // 應該實現: const { reorderByPriority, markTestIncorrect } = useTestQueueStore() markTestIncorrect(currentTestIndex) reorderByPriority() } ``` 3. **responseTimeMs 測量缺失**: ```typescript // 當前:硬編碼 responseTimeMs: 2000 // 應該實現:實際測量 const [startTime, setStartTime] = useState() useEffect(() => { setStartTime(Date.now()) // 測驗開始時記錄 }, [currentTestIndex]) const actualResponseTime = Date.now() - (startTime || 0) ``` ### 建議的改進方向 #### 1. 智能干擾項生成系統 ```typescript interface DistractorGenerationEngine { // 基於詞性的干擾項 generateByPartOfSpeech(word: string, pos: string): string[] // 基於CEFR等級的干擾項 generateByCEFRLevel(word: string, level: string): string[] // 基於語義相似性的干擾項 generateBySemantics(word: string): string[] // 基於用戶歷史錯誤的干擾項 generateByUserMistakes(word: string, userHistory: ErrorHistory[]): string[] } ``` #### 2. 完整的答題分析系統 ```typescript interface AnswerAnalyticsEngine { // 答題時間分析 analyzeResponseTime(startTime: number, endTime: number): ResponseMetrics // 答錯模式分析 categorizeError( userAnswer: string, correctAnswer: string, testType: ReviewMode ): ErrorCategory // 學習建議生成 generateLearningAdvice( errorPattern: ErrorPattern, userProfile: UserProfile ): LearningAdvice[] } ``` #### 3. 自適應難度調整 ```typescript interface AdaptiveDifficultyEngine { // 動態調整測驗難度 adjustDifficulty( currentPerformance: PerformanceMetrics, userProfile: UserProfile ): DifficultyAdjustment // 個性化測驗序列 optimizeTestSequence( remainingTests: TestItem[], userStrongWeakPoints: UserAnalytics ): TestItem[] } ``` ## 📊 性能指標和監控 ### 關鍵性能指標 **渲染性能**: - **組件切換時間**: 目標 <300ms - **答題處理時間**: 目標 <500ms - **狀態更新延遲**: 目標 <100ms **記憶體使用**: - **組件記憶體**: 每個測驗組件 <5MB - **狀態記憶體**: 整體 Store 狀態 <10MB - **清理機制**: 組件卸載時自動清理 **網路性能**: - **答題同步**: 目標 <1秒 - **佇列載入**: 目標 <2秒 - **錯誤重試**: 自動重試 3 次 ### 性能監控實現 ```typescript // 可添加的性能監控邏輯 const usePerformanceMonitoring = () => { const startTime = useRef() useEffect(() => { startTime.current = performance.now() }, [currentTestIndex]) const recordMetrics = useCallback((action: string) => { if (startTime.current) { const duration = performance.now() - startTime.current console.log(`${action} took ${duration.toFixed(2)}ms`) // 可以發送到分析服務 analytics.track('test_performance', { action, duration, testType: currentMode, cardId: currentCard?.id }) } }, [currentMode, currentCard]) return { recordMetrics } } ``` ## 🔮 未來擴展可能性 ### 1. 實時協作學習 ```typescript interface CollaborativeLearning { // 多人同時複習 joinSession(sessionId: string): Promise // 實時同步進度 syncProgress(progress: ProgressState): Promise // 互助提示系統 requestHint(testId: string): Promise provideHint(testId: string, hint: string): Promise } ``` ### 2. AI輔助學習 ```typescript interface AIAssistedLearning { // 智能提示系統 generateHint( testType: ReviewMode, cardData: ReviewCardData, userAttempts: Attempt[] ): LearningHint // 個性化難度 adjustDifficulty( userPerformance: PerformanceHistory, targetAccuracy: number ): DifficultyParams // 學習路徑優化 optimizeLearningPath( userWeaknesses: WeaknessProfile, availableTime: number ): OptimizedPath } ``` ### 3. 多模態學習整合 ```typescript interface MultimodalLearning { // VR/AR 學習環境 enterVRMode(testType: ReviewMode): Promise // 語音評估整合 enableSpeechAssessment(): Promise // 手寫識別 enableHandwritingRecognition(): Promise // 眼動追蹤學習分析 trackLearningAttention(): Promise } ``` ## 🏆 組件設計優勢 ### 架構優勢 1. **模組化設計**: 清晰的職責分離,易於維護和擴展 2. **類型安全**: 完整的 TypeScript 類型定義,編譯時錯誤檢查 3. **狀態管理**: Zustand 提供高效的跨組件狀態同步 4. **性能優化**: useCallback 和條件渲染減少不必要的重新渲染 5. **錯誤處理**: 完整的錯誤邊界和降級方案 ### 開發體驗優勢 1. **開發效率**: 模擬數據模式支援獨立開發 2. **測試友好**: 純函數設計便於單元測試 3. **調試便利**: 詳細的 console.log 和錯誤訊息 4. **擴展性**: 新測驗類型可透過 switch case 輕易添加 ### 學習體驗優勢 1. **即時反饋**: 答題結果立即顯示 2. **進度可視**: 詳細的進度追蹤和統計 3. **智能導航**: 根據答題狀態智能顯示操作選項 4. **容錯機制**: 跳過和重試機制避免學習中斷 ## 🔧 使用指南和最佳實踐 ### 組件使用方式 ```typescript // 在頁面中使用 ReviewRunner import { ReviewRunner } from '@/components/review/ReviewRunner' const ReviewPage = () => { return (
) } ``` ### 自定義測驗類型擴展 ```typescript // 1. 創建新的測驗組件 const NewTestType = ({ cardData, onAnswer, disabled }) => { // 測驗邏輯實現 return
新測驗類型UI
} // 2. 在 ReviewRunner 中添加映射 case 'new-test-type': return ( ) // 3. 更新類型定義 export type ReviewMode = | 'flip-memory' | 'vocab-choice' | 'new-test-type' // 新增 ``` ### Store 狀態訂閱最佳實踐 ```typescript // 精確訂閱,避免不必要重渲染 const currentTest = useTestQueueStore(state => state.testItems[state.currentTestIndex] ) // 避免:訂閱整個 Store const store = useTestQueueStore() // ❌ 會導致所有變化都重渲染 // 推薦:選擇性訂閱 const { currentMode, currentTestIndex } = useTestQueueStore(state => ({ currentMode: state.currentMode, currentTestIndex: state.currentTestIndex })) // ✅ 只有這兩個屬性變化才重渲染 ``` ## 📋 總結 ReviewRunner 是複習系統的**核心控制中樞**,展現了現代 React 應用的最佳實踐: 1. **容器-展示分離**: 邏輯與UI完全分離 2. **狀態管理**: 多Store協作,職責分明 3. **動態渲染**: 基於狀態的智能組件切換 4. **用戶體驗**: 完整的錯誤處理和載入狀態 5. **性能優化**: useCallback和條件渲染優化 6. **可擴展性**: 新測驗類型易於添加 這個組件是複習功能架構設計的精華,體現了**複雜業務邏輯的優雅實現**。 --- *文檔版本: v1.0* *分析對象: ReviewRunner.tsx (440行)* *最後更新: 2025-10-02*