docs(history): React correctness audit + ESLint enforcement (R1-R4)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
null 2026-07-03 19:58:54 -05:00
parent 1387f7c2d7
commit 65c61d71fa
1 changed files with 8 additions and 0 deletions

View File

@ -9,6 +9,14 @@
- **[Tracker] Killed the getTracker N+1 (was ~23 DB round-trips × N bills every home-page load)** — inside `bills.map`, `getTracker` ran a payments query per bill (`fetchPaymentsForBillCycle`) plus `computeAmountSuggestion` per bill, and the suggestion alone fired up to 12 queries per bill (6 months × 2) — roughly 70450 queries for a 35-bill account on every Tracker load. Now one query fetches all bills' cycle payments (grouped in JS by each bill's own range) and two queries compute all amount suggestions (`computeAmountSuggestionsBatch`), replacing the per-bill loops. Behavior-preserving — `tests/amountSuggestionService.test.js` pins the batched suggestion to be byte-identical to the per-bill function, and the `trackerService` tests still pass unchanged. (Tracker P1)
### ✅ React correctness audit + ESLint enforcement
- **[Client] Added ESLint + react-hooks enforcement and fixed everything it found** — there was no linting at all, so nothing enforced the two rules that catch real React bugs across 125 components/pages: **rules-of-hooks** (conditional-hook crashes) and **exhaustive-deps** (stale closures). Added an ESLint 9 flat config (`eslint.config.mjs`) with `eslint-plugin-react-hooks`/`react-refresh`, an `npm run lint` script, and wired it into `npm run ci`. First run: **0 rules-of-hooks violations** (clean), and the rest fixed:
- **Real latent bugs (were runtime-throwing / dead):** `ImportTransactionCsvSection` called `importErrorState()` without importing it (its own error handler would `ReferenceError` on a failed import); a dead duplicate `client/components/MobileTrackerRow.jsx` (unused, with undefined-setter refs) was deleted; `StatusPage`'s `dbOk` had a dead `?? true` branch; plus orphaned dead logic (an unrendered autopay-badge `useMemo`, an unwired categories keyboard handler).
- **All 13 `exhaustive-deps` fixed**, incl. a real stale-value bug: `BankSyncSection`'s save read `btLateGraceDays` outside its `useCallback` deps, so it could persist a stale late-grace value. Others stabilized derived arrays/objects feeding `useMemo` (TrackerPage `filters`/`rows`, HealthPage `bills`, BankTransactionsPage `transactions`) and documented intentional id/filter-scoped effects.
- **Out-of-order response guards (R3):** the month/filter-driven loaders on Analytics, Summary, and Spending now use the same request-sequence guard as the Bank ledger, so a slow response for old params can't overwrite fresher data on rapid navigation.
- Verified-clean (no change needed): event-listener cleanup is balanced everywhere, the always-mounted dialogs' `open`-guarded reset effects are correct, and the CSV preview's index-keys are fine (read-only sample). *(The larger React Query migration of the 7 manual-fetch pages remains as a follow-up enhancement — the async-race correctness is already handled.)*
### 🧹 Bill modal decompose
- **[Bill modal] Broke the 1,733-line God-component into focused sub-components** — `BillModal.jsx` did bill fields + payment CRUD + linked-transaction unmatch + merchant rules + templates + debt/autopay all in one file. Extracted seven presentational components under `client/components/bill-modal/` (`DebtDetailsSection`, `AutopayTrustIndicator`, `PaymentHistoryList`, `PaymentFormFields`, `UnmatchDialogs`, `LinkedTransactionsSection`, `TemplateSection`) plus a shared `transactionDisplay.js` helper module, threading state/handlers via props. State stays in the parent (the save action is unchanged), so it's **behavior-preserving** — each extraction was its own commit, verified with `npm run build` + `npm run test:client`. BillModal is now **1,090 lines (from 1,733, 37%)** and the big interwoven blocks (payments, linked transactions, unmatch flows, debt) are each their own testable file. (Tracker BM2)