Refactor components to eliminate unnecessary useEffect hooks and repl…#9
Conversation
…ace with render-time state adjustments; introduce useIsMounted hook for SSR-safe mounted state management.
|
Need an answer fast? Review this PR in Change Stack to ask focused questions about the PR or a changed range. Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (15)
📝 WalkthroughWalkthroughThis PR systematically replaces ChangesuseEffect to render-time synchronization refactor
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
This PR removes temporary ESLint rule overrides added during the Next.js 16 upgrade by refactoring ~20 hook sites: effect-only derived state is moved to render-time guarded updates, mounted flags are replaced with a new useIsMounted() hook based on useSyncExternalStore, and the remaining legitimate external-store sync effects keep narrowly-scoped ESLint disables.
Changes:
- Add
useIsMounted()(SSR/hydration-safe) and migrate mounted-guard patterns to it. - Refactor multiple components to remove “sync derived state in useEffect” patterns (using guarded render-time updates, refs, or memoized derivations).
- Remove global ESLint rule overrides and keep only scoped disables where effects are truly required.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/hooks/use-is-mounted.ts | New useSyncExternalStore-based mounted hook for SSR/hydration safety. |
| src/components/welcome-dialog-wrapper.tsx | Opens dialog based on showHelp via guarded render-time update; keeps effect for URL cleanup. |
| src/components/ui/searchable-mission-selector.tsx | Converts retry handlers to use latest-callback refs; scopes effect lint disables for data fetching. |
| src/components/theme-selector.tsx | Replaces mounted state/effect with useIsMounted(). |
| src/components/radar-view-dialog.tsx | Avoids immediate effect-body state set by generating noise pattern on first interval tick. |
| src/components/quest-board-client.tsx | Documents and scopes ESLint disable for intentional external-store synchronization effect. |
| src/components/new-quest-dialog.tsx | Replaces form init and default-deadline effects with guarded render-time updates. |
| src/components/live-time.tsx | Replaces mounted + timezone effects with useIsMounted() + useSyncExternalStore snapshot. |
| src/components/focus-mode/InterruptCapture.tsx | Moves modal-open reset logic to guarded render-time update; effect handles focus + timer with cleanup. |
| src/components/focus-mode/FocusTimer.tsx | Refactors timer resets and guards to avoid effect-only derived state; uses refs for write-only guard. |
| src/components/edit-mission-dialog.tsx | Moves mission->form synchronization to guarded render-time update. |
| src/components/archive/filters/quest-filters.tsx | Moves external reset sync to guarded render-time update. |
| src/components/archive/date-range-selector.tsx | Derives selected preset via memo; changes range->tempRange syncing logic. |
| src/components/archive-mission-dialog.tsx | Moves open/close reset logic to guarded render-time update. |
| eslint.config.mjs | Removes global disables for react-hooks/set-state-in-effect and react-hooks/immutability. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const loadMissionsRef = useRef<() => void>(undefined); | ||
| const performSearchRef = useRef<(query: string) => void>(undefined); |
| // Sync tempRange when the incoming range changes (render-time). | ||
| const [prevRange, setPrevRange] = useState(range); | ||
| if (range !== prevRange) { | ||
| setPrevRange(range); | ||
| setTempRange(range); |
Removes eslint rule overrides added during the Next.js 16 upgrade and fixes the ~20 flagged sites: effects that only synced derived state are converted to render-time derived-state guards (prevX pattern), mounted flags replaced with useSyncExternalStore via a new useIsMounted hook, write-only guard flags converted to refs, and two genuine external-store sync effects kept with scoped eslint-disable comments.
Summary by CodeRabbit
Refactor
Chores