Skip to content

fix(lotw): stop /lotw page from refetching in an infinite loop#208

Merged
patrickrb merged 1 commit into
mainfrom
fix/lotw-page-render-loop
May 12, 2026
Merged

fix(lotw): stop /lotw page from refetching in an infinite loop#208
patrickrb merged 1 commit into
mainfrom
fix/lotw-page-render-loop

Conversation

@patrickrb
Copy link
Copy Markdown
Owner

Summary

The /lotw page hammered /api/stations, /api/lotw/upload, and /api/lotw/download continuously after mount. User reported it visually in Network tab; root cause is a triple-cycle React deps tangle:

  1. useEffect deps included loading and loadData.
  2. loadData calls setLoading(true) on entry and setLoading(false) on finish — every flip retriggers the effect.
  3. loadData's useCallback deps included selectedStation, and loadData itself called setSelectedStation(...) to pick a default — that state change reissues loadData's identity, which is also a useEffect dep.

Each of #1 or #2/#3 alone produces an infinite loop. Combined: continuous refetches.

Fix

  • loadData useCallback deps now []. The default-station selection uses setSelectedStation((prev) => prev || derive(fetched)) so it doesn't depend on the current selectedStation value.
  • useEffect no longer depends on this component's loading — only on user, userLoading from UserContext, router, and the now-stable loadData.
  • Auth-gate honors UserContext.loading so it doesn't bounce to /login during the initial /api/user check (user is null both pre-resolve and when unauthenticated).

Net: page mounts → exactly one fetch round → idle. The manual Refresh button and post-action reloads still call loadData explicitly.

Audit

Grepped the rest of src/app/**/page.tsx for the same pattern — /lotw was the only page with this exact tangle, so this is a one-file change.

Test plan

  • npm run typecheck clean
  • npm run lint clean
  • After deploy: open /lotw with DevTools → Network filtered to lotw|stations and confirm only one round of requests fires on mount; further requests only happen when clicking Refresh or after Upload/Download actions.

🤖 Generated with Claude Code

The /lotw page hammered /api/stations, /api/lotw/upload, and
/api/lotw/download continuously because of a triple-cycle dependency
tangle:

  1. useEffect deps include `loading` and `loadData`
  2. loadData calls setLoading(true) on entry, setLoading(false) on
     finish — flipping `loading` each call retriggers useEffect
  3. loadData's useCallback deps include `selectedStation`, and
     loadData calls setSelectedStation(...) to pick a default — the
     state change reissues loadData's identity, which is also a dep
     of useEffect

Each of #1 or #2/#3 alone is enough to loop. Combined, the page
re-rendered constantly and hit three API endpoints on each pass.

Fix:
- loadData useCallback deps = [] (no longer a function of selectedStation)
- setSelectedStation uses a functional updater so it can derive the
  default from the freshly-fetched stations without depending on
  selectedStation in deps
- useEffect waits on UserContext's `loading` (not this component's),
  so the auth gate doesn't redirect during the initial /api/user check

Net: page mounts → one fetch round → idle. Manual refresh button and
post-action reloads still call loadData explicitly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 12, 2026

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

Project Deployment Actions Updated (UTC)
nodelog Ready Ready Preview, Comment May 12, 2026 0:16am

Request Review

@patrickrb patrickrb merged commit 60fa5c7 into main May 12, 2026
7 checks passed
@patrickrb patrickrb deleted the fix/lotw-page-render-loop branch May 12, 2026 00:20
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