8.5 KiB
8.5 KiB
狀態管理系統文件
📋 概述
這個目錄包含了應用程式的狀態管理系統,採用 Zustand 作為狀態管理工具。系統被設計為模組化架構,將原本單一巨大的 store 拆分為多個專門化的 stores,每個都有明確的職責範圍。
🏗️ 架構設計
設計原則
- 單一職責原則: 每個 store 只負責特定的狀態域
- 最小重渲染: 組件只訂閱需要的狀態,避免不必要的重渲染
- 型別安全: 使用 TypeScript 確保型別安全
- 可測試性: 小型、專注的 stores 更容易測試
Store 分類
/store/
├── useReviewSessionStore.ts # 會話狀態管理
├── useTestQueueStore.ts # 測試隊列管理
├── useTestResultStore.ts # 測試結果管理
├── useReviewDataStore.ts # 數據狀態管理
└── useUIStore.ts # UI 狀態管理
📚 各 Store 詳細說明
1. useReviewSessionStore.ts
職責: 管理複習會話的核心狀態
狀態內容
interface ReviewSessionState {
// 核心會話狀態
mounted: boolean // 組件是否已掛載
isLoading: boolean // 是否正在載入
error: string | null // 錯誤訊息
// 當前卡片狀態
currentCard: ExtendedFlashcard | null // 當前顯示的詞卡
currentCardIndex: number // 當前卡片索引
}
主要功能
- 會話生命週期管理: 控制會話的開始、結束
- 當前卡片追蹤: 追蹤使用者正在學習的詞卡
- 錯誤處理: 統一管理會話相關錯誤
使用範例
const { currentCard, error, setCurrentCard } = useReviewSessionStore()
// 設置當前卡片
setCurrentCard(newCard)
2. useTestQueueStore.ts
職責: 管理測試隊列和測試流程
狀態內容
interface TestQueueState {
testItems: TestItem[] // 測試項目清單
currentTestIndex: number // 當前測試索引
completedTests: number // 已完成測試數量
totalTests: number // 總測試數量
currentMode: ReviewMode // 當前測試模式
}
主要功能
- 測試隊列初始化: 根據詞卡和已完成測試建立隊列
- 測試進度管理: 追蹤測試進度和完成狀態
- 測試流程控制: 控制測試的前進、跳過等操作
核心方法
// 初始化測試隊列
initializeTestQueue(dueCards, completedTests)
// 進入下一個測試
goToNextTest()
// 跳過當前測試
skipCurrentTest()
// 標記測試完成
markTestCompleted(testIndex)
測試類型
flip-memory: 翻卡記憶vocab-choice: 詞彙選擇vocab-listening: 詞彙聽力sentence-listening: 例句聽力sentence-fill: 例句填空sentence-reorder: 例句重組sentence-speaking: 例句口說
3. useTestResultStore.ts
職責: 管理測試結果和分數統計
狀態內容
interface TestResultState {
score: { correct: number; total: number } // 分數統計
isRecordingResult: boolean // 是否正在記錄結果
recordingError: string | null // 記錄錯誤
}
主要功能
- 分數追蹤: 記錄正確和總答題數
- 結果記錄: 將測試結果發送到後端
- 統計計算: 提供準確率等統計資訊
核心方法
// 更新分數
updateScore(isCorrect: boolean)
// 記錄測試結果到後端
recordTestResult({
flashcardId,
testType,
isCorrect,
userAnswer,
confidenceLevel,
responseTimeMs
})
// 獲取準確率
getAccuracyPercentage()
4. useReviewDataStore.ts
職責: 管理複習數據和UI顯示狀態
狀態內容
interface ReviewDataState {
dueCards: ExtendedFlashcard[] // 到期詞卡清單
showComplete: boolean // 是否顯示完成畫面
showNoDueCards: boolean // 是否顯示無詞卡畫面
isLoadingCards: boolean // 是否正在載入詞卡
loadingError: string | null // 載入錯誤
}
主要功能
- 詞卡資料管理: 載入和管理到期的詞卡
- UI 狀態控制: 控制不同UI狀態的顯示
- 資料快取: 快取詞卡資料避免重複請求
核心方法
// 載入到期詞卡
loadDueCards()
// 根據ID查找詞卡
findCardById(cardId)
// 獲取詞卡數量
getDueCardsCount()
5. useUIStore.ts
職責: 管理全域UI狀態
狀態內容
interface UIState {
showTaskListModal: boolean // 任務清單Modal
showReportModal: boolean // 錯誤回報Modal
modalImage: string | null // 圖片Modal
reportReason: string // 回報原因
reportingCard: any | null // 正在回報的詞卡
isAutoSelecting: boolean // 自動選擇狀態
}
🔄 Store 之間的協作
資料流向
graph TD
A[useReviewDataStore] -->|詞卡資料| B[useTestQueueStore]
B -->|當前測試| C[useReviewSessionStore]
C -->|測試互動| D[useTestResultStore]
D -->|結果回饋| B
E[useUIStore] -.->|UI狀態| A
E -.->|UI狀態| B
E -.->|UI狀態| C
E -.->|UI狀態| D
協作流程
-
初始化階段:
useReviewDataStore載入到期詞卡useTestQueueStore根據詞卡建立測試隊列useReviewSessionStore設置當前詞卡
-
測試階段:
useReviewSessionStore管理當前測試狀態useTestResultStore記錄測試結果useTestQueueStore控制測試進度
-
完成階段:
useTestQueueStore檢查是否完成所有測試useReviewDataStore顯示完成狀態
🎯 使用最佳實踐
1. 選擇性訂閱
// ❌ 避免:訂閱整個 store
const store = useReviewSessionStore()
// ✅ 推薦:只訂閱需要的狀態
const { currentCard, error } = useReviewSessionStore()
2. 狀態更新模式
// ✅ 推薦:使用專門的 actions
const { setCurrentCard } = useReviewSessionStore()
setCurrentCard(newCard)
// ❌ 避免:直接修改狀態
// store.currentCard = newCard // 這樣不會觸發重渲染
3. 錯誤處理
// ✅ 推薦:檢查錯誤狀態
const { error, isLoading } = useReviewSessionStore()
if (error) {
return <ErrorComponent message={error} />
}
if (isLoading) {
return <LoadingComponent />
}
🧪 測試策略
單元測試
// 測試 store 的 actions
describe('useTestResultStore', () => {
it('should update score correctly', () => {
const { updateScore, score } = useTestResultStore.getState()
updateScore(true)
expect(score.correct).toBe(1)
expect(score.total).toBe(1)
})
})
整合測試
// 測試多個 stores 的協作
describe('Review Flow Integration', () => {
it('should coordinate between stores correctly', () => {
// 測試資料載入 → 隊列建立 → 測試執行的流程
})
})
🔧 開發工具
Zustand DevTools
import { subscribeWithSelector, devtools } from 'zustand/middleware'
export const useReviewSessionStore = create<ReviewSessionState>()(
devtools(
subscribeWithSelector((set, get) => ({
// store implementation
})),
{ name: 'review-session-store' }
)
)
📈 效能考量
重渲染優化
- 狀態分離: 不相關的狀態變更不會觸發組件重渲染
- 選擇性訂閱: 組件只訂閱需要的狀態片段
- 記憶化: 使用
useMemo和useCallback優化計算
記憶體管理
- 自動清理: stores 會在適當時機重置狀態
- 垃圾回收: 移除不再需要的資料引用
🚀 未來擴展
新增 Store
- 建立新的 store 檔案
- 定義 interface 和初始狀態
- 實作 actions 和 getters
- 加入適當的 TypeScript 型別
- 更新文件
Store 拆分指導原則
- 當 store 超過 150 行時考慮拆分
- 根據業務邏輯邊界進行拆分
- 確保拆分後的 stores 職責清晰
📞 支援
如有問題或需要協助,請參考:
- Zustand 官方文件
- TypeScript 最佳實踐
- 團隊內部技術文件
維護者: 開發團隊 最後更新: 2025-09-28