[PR #1834] [MERGED] fix(staffml): pause Nav due-count poll on hidden tabs + sync across tabs #36457

Closed
opened 2026-07-16 00:20:29 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/harvard-edge/cs249r_book/pull/1834
Author: @farhan523
Created: 6/3/2026
Status: Merged
Merged: 6/10/2026
Merged by: @profvjreddi

Base: devHead: fix/nav-pause-polling-on-hidden-tab


📝 Commits (1)

  • 4f31ebd fix(staffml): pause Nav due-count poll on hidden tabs + sync across tabs

📊 Changes

3 files changed (+244 additions, -8 deletions)

View changed files

interviews/staffml/src/__tests__/use-visibility-poll.test.tsx (+144 -0)
📝 interviews/staffml/src/components/Nav.tsx (+26 -8)
interviews/staffml/src/lib/hooks/useVisibilityPoll.ts (+74 -0)

📄 Description

Summary

Nav was polling getDueCount every 30s indefinitely via a bare setInterval, even on backgrounded tabs. Cheap per call (it reads localStorage), but the unnecessary wakeups burn battery on mobile and add no value when no one is looking. There was also no cross-tab sync: a card completed in one tab wouldn't update the badge in another tab until that other tab's next 30s tick.

Changes

  • New hook src/lib/hooks/useVisibilityPoll.ts: pauses the interval on visibilitychange→hidden, re-arms on →visible with an immediate catch-up tick. Callback is held in a ref so a fresh closure on every render is picked up without re-arming.
  • Throw-isolation inside the hook: each tick is wrapped in try/catch with console.error. Without this, a throwing callback on the mount-time synchronous tick propagates out of the effect body before React receives the cleanup — the already-armed setInterval would orphan and re-throw every interval forever.
  • Cross-tab freshness in Nav: the hook is explicit in its docs that it is not a cross-tab sync primitive (only catches up on visibility transitions, not on two-tabs-both-visible). So Nav adds a separate storage event listener that fires in other tabs the instant one writes to localStorage.
  • Replace Nav's empty catch {} with console.error(...) so a real failure (corrupted localStorage, JSON parse error) surfaces in devtools instead of presenting as a frozen badge. Refresh logic hoisted into one useCallback shared by both the poll and the storage handler.

Test plan

  • npx vitest run → 65/65 individual cases (was 57 on dev; +8 new for the hook).
  • npx tsc --noEmit → unchanged from dev baseline (the 2 pre-existing katex / js-quantities errors are env-local: those packages are declared in package.json but missing from node_modules until npm install runs).
  • Manual: open Nav in two browser windows, complete an SR card in window A, observe the due-count badge in window B drop within ~1 tick (the storage event fires immediately; the visibility poll would otherwise wait up to 30s).
  • Manual: open DevTools → Application → throttle background tab to confirm no setInterval wakeups while the tab is hidden.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/harvard-edge/cs249r_book/pull/1834 **Author:** [@farhan523](https://github.com/farhan523) **Created:** 6/3/2026 **Status:** ✅ Merged **Merged:** 6/10/2026 **Merged by:** [@profvjreddi](https://github.com/profvjreddi) **Base:** `dev` ← **Head:** `fix/nav-pause-polling-on-hidden-tab` --- ### 📝 Commits (1) - [`4f31ebd`](https://github.com/harvard-edge/cs249r_book/commit/4f31ebda1ab0316f52f8168d58ca319e20197324) fix(staffml): pause Nav due-count poll on hidden tabs + sync across tabs ### 📊 Changes **3 files changed** (+244 additions, -8 deletions) <details> <summary>View changed files</summary> ➕ `interviews/staffml/src/__tests__/use-visibility-poll.test.tsx` (+144 -0) 📝 `interviews/staffml/src/components/Nav.tsx` (+26 -8) ➕ `interviews/staffml/src/lib/hooks/useVisibilityPoll.ts` (+74 -0) </details> ### 📄 Description ## Summary Nav was polling `getDueCount` every 30s indefinitely via a bare `setInterval`, even on backgrounded tabs. Cheap per call (it reads localStorage), but the unnecessary wakeups burn battery on mobile and add no value when no one is looking. There was also no cross-tab sync: a card completed in one tab wouldn't update the badge in another tab until that other tab's next 30s tick. ### Changes - **New hook** `src/lib/hooks/useVisibilityPoll.ts`: pauses the interval on `visibilitychange→hidden`, re-arms on `→visible` with an immediate catch-up tick. Callback is held in a ref so a fresh closure on every render is picked up without re-arming. - **Throw-isolation inside the hook**: each tick is wrapped in `try/catch` with `console.error`. Without this, a throwing callback on the mount-time synchronous tick propagates out of the effect body *before* React receives the cleanup — the already-armed `setInterval` would orphan and re-throw every interval forever. - **Cross-tab freshness in Nav**: the hook is explicit in its docs that it is **not** a cross-tab sync primitive (only catches up on visibility transitions, not on two-tabs-both-visible). So Nav adds a separate `storage` event listener that fires in *other* tabs the instant one writes to localStorage. - **Replace `Nav`'s empty `catch {}` with `console.error(...)`** so a real failure (corrupted localStorage, JSON parse error) surfaces in devtools instead of presenting as a frozen badge. Refresh logic hoisted into one `useCallback` shared by both the poll and the storage handler. ## Test plan - [x] `npx vitest run` → 65/65 individual cases (was 57 on dev; +8 new for the hook). - [x] `npx tsc --noEmit` → unchanged from dev baseline (the 2 pre-existing `katex` / `js-quantities` errors are env-local: those packages are declared in `package.json` but missing from `node_modules` until `npm install` runs). - [ ] Manual: open Nav in two browser windows, complete an SR card in window A, observe the due-count badge in window B drop within ~1 tick (the `storage` event fires immediately; the visibility poll would otherwise wait up to 30s). - [ ] Manual: open DevTools → Application → throttle background tab to confirm no `setInterval` wakeups while the tab is hidden. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-07-16 00:20:29 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/cs249r_book#36457