Skip to content

套用函數式思考原則重構:分離動作與計算、減少可變迴圈 - #9

Merged
Retsomm merged 1 commit into
mainfrom
dev
Jul 12, 2026
Merged

Retsomm merged 1 commit into
mainfrom
dev

Conversation

@Retsomm

@Retsomm Retsomm commented Jul 12, 2026 •

Copy link
Copy Markdown
Owner
  • chapters.ts:抽出攤平題目清單當抽象屏障,取代重複三層 for 迴圈
  • App.tsx:navGroupOfChapter 改用 find;畫面渲染改成無可變賦值的 switch 表達式
  • Mascot.tsx:getStage 改用 reduce
  • useProgress.ts:migrateWrongIds 改用 Object.fromEntries+map
  • CodeBlock.tsx:highlight 改用 matchAll+reduce 取代 while 迴圈手動維護多個可變變數
  • answer/route.ts:把「決定要寫入什麼」拆成宣告式運算式,跟「執行交易」的動作分開

Summary by CodeRabbit

  • Refactor
    • Improved internal handling of chapter, level, question, and progress lookups.
    • Streamlined screen selection and navigation logic without changing the available screens.
    • Simplified code highlighting and mascot progression calculations.
    • Preserved existing progress tracking, quiz behavior, and saved/wrong-question functionality.

- chapters.ts:抽出攤平題目清單當抽象屏障,取代重複三層 for 迴圈
- App.tsx:navGroupOfChapter 改用 find;畫面渲染改成無可變賦值的 switch 表達式
- Mascot.tsx:getStage 改用 reduce
- useProgress.ts:migrateWrongIds 改用 Object.fromEntries+map
- CodeBlock.tsx:highlight 改用 matchAll+reduce 取代 while 迴圈手動維護多個可變變數
- answer/route.ts:把「決定要寫入什麼」拆成宣告式運算式,跟「執行交易」的動作分開

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
easy-learn Ready Ready Preview, Comment Jul 12, 2026 2:16am

@coderabbitai

coderabbitai Bot commented Jul 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The changes refactor chapter/question lookups, progress migration and answer writes, screen selection, syntax highlighting, and mascot stage selection toward functional collection operations while preserving existing public APIs and behavior.

Changes

Core application refactors

Layer / File(s) Summary
Question indexing and progress migration
src/data/chapters.ts, src/hooks/useProgress.ts
Question helpers now use flattened question data, while legacy wrong-question identifiers are migrated with Object.fromEntries.
Answer persistence transaction
src/app/api/progress/answer/route.ts
Wrong-entry, daily-stat, and chapter-stat writes are composed into a single Prisma transaction input.
Screen selection and navigation
src/App.tsx
Chapter lookup uses find/some, and view content uses switch-based selection with precomputed quiz data.
Utility implementation rewrites
src/components/CodeBlock.tsx, src/components/Mascot.tsx
Syntax highlighting uses matchAll/reduce, and mascot stages use reduce selection.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • Retsomm/EasyLearn#2: Adds chapter-level data exercised by the refactored chapter and question lookup helpers.
  • Retsomm/EasyLearn#3: Extends the wrong-question flow that uses the refactored wrong-question lookups and migration path.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 標題準確概括了此次以函數式手法重構、減少可變迴圈與分離計算/動作的主要變更。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/data/chapters.ts (1)

70-71: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider caching the flattened levels array.

getLevel calls chapters.flatMap((ch) => ch.levels) on every invocation, allocating a new array each time. Since it's called on every render in App.tsx when in quiz view, you could cache the flattened levels at module scope (like flatQuestions) for consistency and to avoid repeated allocation.

♻️ Optional: cache flattened levels
+const flatLevels: Level[] = chapters.flatMap((ch) => ch.levels)
+
 export const getLevel = (levelId: string): Level | null =>
-  chapters.flatMap((ch) => ch.levels).find((l) => l.id === levelId) ?? null
+  flatLevels.find((l) => l.id === levelId) ?? null
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/data/chapters.ts` around lines 70 - 71, Cache the flattened levels array
at module scope, alongside the existing flatQuestions cache, and update getLevel
to search that cached collection instead of calling chapters.flatMap on every
invocation.
src/components/CodeBlock.tsx (1)

9-23: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Optional: avoid O(n²) spread in reduce.

...acc.parts on each iteration copies the entire accumulator, making this quadratic in the number of matches. For typical code snippets this is negligible, but if you want to keep it functional without the cost, consider collecting into a flat array via concat:

♻️ Optional refactor using concat
   const { parts, last } = matches.reduce<{ parts: ReactNode[]; last: number }>(
     (acc, m, i) => {
       const [text, comment, string, keyword, number] = m
       const cls = comment ? 'tok-comment' : string ? 'tok-string' : keyword ? 'tok-keyword' : number ? 'tok-number' : ''
       const gap = m.index > acc.last ? [code.slice(acc.last, m.index)] : []
       return {
-        parts: [
-          ...acc.parts,
-          ...gap,
-          <span key={i} className={cls}>
-            {text}
-          </span>,
-        ],
+        parts: acc.parts.concat(gap, [
+          <span key={i} className={cls}>
+            {text}
+          </span>,
+        ]),
         last: m.index + text.length,
       }
     },
     { parts: [], last: 0 },
   )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/CodeBlock.tsx` around lines 9 - 23, Optionally refactor the
matches.reduce accumulator in CodeBlock so each iteration avoids spreading the
full acc.parts array; append the gap and highlighted span through concat or
equivalent in-place collection while preserving output order and the existing
last-index tracking.
src/App.tsx (1)

72-191: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Switch IIFE and quizLevel guard look correct.

The early return on line 75 safely guarantees quizLevel is non-null inside the case 'quiz' branch, making the quizLevel! assertions on lines 83 and 89 safe. All 11 View variants are covered by the switch, with default correctly mapping to Home.

One optional improvement: an explicit case 'home': instead of relying on default would give you compile-time exhaustiveness checking if a new view variant is added later.

♻️ Optional: explicit home case for exhaustiveness
       case 'profile':
         return <Profile progress={progress} />
-      default:
+      case 'home':
         return (
           <Home
             progress={progress}
             onOpenChapter={(chapterId) => setView({ name: 'levellist', chapterId })}
             onMixedPractice={startMixedPractice}
           />
         )
+      default:
+        const _exhaustive: never = view
+        throw new Error(`Unhandled view: ${JSON.stringify(_exhaustive)}`)
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/App.tsx` around lines 72 - 191, Optionally replace the fallback default
branch in the content switch with an explicit case 'home' branch returning Home,
while preserving the existing Home props and behavior; use an exhaustiveness
check for any unhandled View variants so future additions are caught at compile
time.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/App.tsx`:
- Around line 72-191: Optionally replace the fallback default branch in the
content switch with an explicit case 'home' branch returning Home, while
preserving the existing Home props and behavior; use an exhaustiveness check for
any unhandled View variants so future additions are caught at compile time.

In `@src/components/CodeBlock.tsx`:
- Around line 9-23: Optionally refactor the matches.reduce accumulator in
CodeBlock so each iteration avoids spreading the full acc.parts array; append
the gap and highlighted span through concat or equivalent in-place collection
while preserving output order and the existing last-index tracking.

In `@src/data/chapters.ts`:
- Around line 70-71: Cache the flattened levels array at module scope, alongside
the existing flatQuestions cache, and update getLevel to search that cached
collection instead of calling chapters.flatMap on every invocation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bc335efe-a684-4394-b54f-4e2368fec16f

📥 Commits

Reviewing files that changed from the base of the PR and between 877bce3 and d7a2e5b.

📒 Files selected for processing (6)
  • src/App.tsx
  • src/app/api/progress/answer/route.ts
  • src/components/CodeBlock.tsx
  • src/components/Mascot.tsx
  • src/data/chapters.ts
  • src/hooks/useProgress.ts

@Retsomm
Retsomm merged commit 2931137 into main Jul 12, 2026
3 checks passed

This branch was successfully deployed

1 active deployment
Preview — d7a2e5ba Deployed Jul 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant