Skip to content

fix: resolve eslint react-hooks/set-state-in-effect violations - #22

Merged
Yuvraj-Sarathe merged 5 commits into
Yuvraj-Sarathe:mainfrom
Yuva-Deekshitha-N:fix/eslint-set-state-in-effect
Jul 26, 2026
Merged

fix: resolve eslint react-hooks/set-state-in-effect violations#22
Yuvraj-Sarathe merged 5 commits into
Yuvraj-Sarathe:mainfrom
Yuva-Deekshitha-N:fix/eslint-set-state-in-effect

Conversation

@Yuva-Deekshitha-N

@Yuva-Deekshitha-N Yuva-Deekshitha-N commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

🔗 Related Issue

Closes #11


📝 Description of Changes

ESLint's react-hooks/set-state-in-effect rule was failing on every PR because three files had unconditional setState calls inside useEffect. This is a repo-wide CI blocker, not introduced by any contributor.

Root cause: Calling setState unconditionally inside useEffect can trigger cascading renders (effect fires → setState → re-render → effect fires again). ESLint flags this to prevent subtle render loops.

Fix applied: Replaced all offending patterns with React's idiomatic "derived state during render" approach — track the previous dependency value in a second useState, compare during render, and call setState conditionally in the render body (not inside an effect). This is the pattern recommended in the React docs for syncing state to a changing prop/dependency.

Files fixed:

  • hooks/useLoadingMessage.ts — removed useEffect that reset index to 0; replaced with a prevShuffled state comparison during render.
  • app/settings/page.tsx — removed useEffect that synced localSettings from store; replaced with a prevSettings state comparison during render.
  • app/assessment/page.tsx — added value-equality guards before setShowCompletenessWarning and setSavedCandidates in the existing render-time derived-state blocks so setState only fires when the value actually changes.

🏷️ Proposed Labels

  • UI/UX
  • Documentation
  • CI/CD
  • Backend Logic
  • Anything else

📂 Core Files Changed

  • hooks/useLoadingMessage.ts
  • app/settings/page.tsx
  • app/assessment/page.tsx

📸 Verification & Screenshots

  • UI/UX: No visual changes. All UI behaviour is identical — loading messages still cycle, settings still sync, completeness warning still appears/dismisses correctly.

  • CI/CD: npm run lint — exit code 0, zero errors (two pre-existing warnings remain, unrelated to this fix). npm run build — clean production build, no type errors.


🤖 AI Assistance Declaration

Did you use an AI tool to write or assist with this code OR Pull Request?

  • Yes
  • No (If no, you can skip the rest of this section)

⚠️ IF YOU CHECKED "YES", YOU MUST ANSWER THE FOLLOWING:

** Which AI Model did you use?: Amazon Q Developer

** Which Platform/Tool?: Amazon Q (VS Code plugin)

** What exactly did the AI do?:
Identified the three offending files and patterns, proposed the derived-state-during-render fix, applied the code edits, ran ESLint after each change to verify, and iterated when the first attempt (using useRef during render) triggered a second lint rule (react-hooks/refs).

** What exactly did YOU do?:
Identified this as a repo-wide CI blocker affecting all contributors, provided the ESLint rule name and affected file list, reviewed each proposed fix for correctness, and verified the final lint output showed zero errors before committing.

** What is the advantage of using this AI approach here?:
The fix required understanding two separate ESLint rules (set-state-in-effect and refs) and their interaction. AI accelerated the iteration loop — first attempt used refs (wrong), second attempt used state-based previous-value tracking (correct) — without needing to manually re-run lint between each attempt.

⚠️ Reviewer Notes

  • components/DotField.tsx is listed in the issue description as having an unused eslint-disable directive, but the file contains no such comment in the current main. No change was made to it.
  • The two @next/next/no-img-element warnings in app/assessment/page.tsx (lines 820, 906) are pre-existing and out of scope for this fix.
  • All three fixes use the same pattern so the codebase stays consistent.

✅ The "I Swear I Didn't Break Anything" Pledge

  • I have thoroughly tested these changes in my own local branch.
  • I verified multiple times that this code compiles into a standalone build and does not break existing production features.

Summary by Sourcery

Resolve ESLint react-hooks/set-state-in-effect violations by moving state synchronization logic out of effects into render-time derived state patterns.

Bug Fixes:

  • Prevent potential render loops and lint failures caused by unconditional setState calls inside useEffect in assessment and settings pages and loading message hook.

Enhancements:

  • Introduce render-time derived state patterns using previous-value tracking for completeness warnings, saved candidates, loading message index, and settings synchronization to keep behaviour consistent while satisfying lint rules.

CI:

  • Restore clean ESLint runs by eliminating react-hooks/set-state-in-effect rule violations across the affected files.

Summary by CodeRabbit

  • Bug Fixes

    • Improved assessment completeness warnings so they react consistently to updated assessment details, including reliable dismissal.
    • Improved saving of assessed candidates during the assessment flow to keep comparison data available for the current session.
  • Refactor

    • Streamlined assessment warning and candidate persistence logic to reduce extra state updates.
    • Updated settings page behavior to avoid unintended local settings resync after external changes.
    • Refined loading message rotation for more predictable message sequencing.

@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

@Yuva-Deekshitha-N is attempting to deploy a commit to the Yuvraj Sarathe's projects Team on Vercel.

A member of the Team first needs to authorize it.

@sourcery-ai

sourcery-ai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Refactors three components/hooks to eliminate ESLint react-hooks/set-state-in-effect violations by moving setState logic out of useEffect into render-time derived state guarded by previous-value tracking, ensuring state updates are conditional and avoiding render loops.

Sequence diagram for render-time derived state in AssessmentContent

sequenceDiagram
  participant React
  participant AssessmentContent
  participant sessionStorage

  React->>AssessmentContent: render()
  alt assessment_changed
    AssessmentContent->>AssessmentContent: setShowCompletenessWarning(hasMissing)
  end
  alt assessment_and_githubData_changed
    AssessmentContent->>sessionStorage: getItem(assessedCandidates)
    AssessmentContent->>sessionStorage: setItem(assessedCandidates, JSON.stringify(stored))
    AssessmentContent->>AssessmentContent: setSavedCandidates(stored)
  end
Loading

File-Level Changes

Change Details Files
Derive the loading message index from the shuffled messages during render, tracking the previous shuffled value to reset the index only when the message set actually changes.
  • Reordered useLoadingMessage hook state initialization so shuffled messages are memoized before index state is created.
  • Introduced prevShuffled state to store the last shuffled messages array.
  • Replaced the useEffect-based index reset with a render-time conditional that compares shuffled to prevShuffled and conditionally resets index to 0.
hooks/useLoadingMessage.ts
Synchronize local settings state with the store settings via render-time comparison rather than an effect, using a previous-settings state to only update when the store replaces the settings object.
  • Removed the useEffect that unconditionally copied settings into localSettings whenever settings changed.
  • Added prevSettings state initialized from settings to track the last seen store settings object.
  • Added a render-time conditional that updates prevSettings and localSettings only when settings !== prevSettings.
app/settings/page.tsx
Move completeness warning and saved candidates derivation out of effects into guarded render-time blocks that track previous assessment/githubData values and only call setState when the derived values actually change.
  • Added refs to track previous assessment and GitHub data values used when deriving completeness warning and saved candidates.
  • Replaced the useEffect that derived showCompletenessWarning from assessment with a render-time conditional that updates the warning only when assessment changes and the derived hasMissing flag differs from the current warning state.
  • Replaced the useEffect that read and wrote assessedCandidates in sessionStorage and updated savedCandidates with a render-time block that syncs session storage and savedCandidates only when assessment/githubData change and the derived candidates list differs from the current state.
app/assessment/page.tsx

Assessment against linked issues

Issue Objective Addressed Explanation
#11 Resolve ESLint react-hooks/set-state-in-effect violations so PR Verification CI (lint/tests) no longer fails on app/assessment/page.tsx, app/settings/page.tsx, and hooks/useLoadingMessage.ts.
#11 Remove the unused eslint-disable directive in components/DotField.tsx that is mentioned in the issue.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d3dc749-0ca6-4cd9-9949-ddcd9bd96220

📥 Commits

Reviewing files that changed from the base of the PR and between 8af3d73 and d377266.

📒 Files selected for processing (3)
  • app/assessment/page.tsx
  • app/settings/page.tsx
  • hooks/useLoadingMessage.ts

📝 Walkthrough

Walkthrough

The changes derive assessment warning visibility from current data and dismissal state, persist assessed candidates immediately after generation, remove settings synchronization effects, and refactor loading-message rotation to use combined state.

Changes

React state and effect cleanup

Layer / File(s) Summary
Assessment warning and candidate persistence
app/assessment/page.tsx
Completeness warnings are memoized from assessment data and dismissal state; assessed candidates are upserted after assessment generation and written to sessionStorage.
Settings state initialization
app/settings/page.tsx
The effect synchronizing local settings from external store replacements is removed.
Loading-message state progression
hooks/useLoadingMessage.ts
Message-list and index state are combined, removing the explicit index-reset effect while retaining interval-based advancement.

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

Possibly related PRs

Suggested reviewers: yuvraj-sarathe

🚥 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 The title clearly and concisely describes the main change: fixing react-hooks/set-state-in-effect ESLint violations.
Linked Issues check ✅ Passed The PR addresses the linked issue by refactoring the three reported files to remove the set-state-in-effect lint violations.
Out of Scope Changes check ✅ Passed The changes stay within the lint-fix scope; the extra import reorder and comment are minor and related.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

app/assessment/page.tsx

Oops! Something went wrong! :(

ESLint: 9.39.1

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'configs' -> object with constructor 'Object'
| property 'flat' -> object with constructor 'Object'
| ...
| property 'plugins' -> object with constructor 'Object'
--- property 'react' closes the circle
Referenced from: /.eslintrc.json
at JSON.stringify ()
at /node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2255:45
at Array.map ()
at ConfigValidator.formatErrors (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2246:23)
at ConfigValidator.validateConfigSchema (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2277:84)
at ConfigArrayFactory._normalizeConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3123:19)
at ConfigArrayFactory._loadConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3088:21)
at ConfigArrayFactory._loadExtendedShareableConfig (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3393:21)
at ConfigArrayFactory._loadExtends (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3261:25)
at ConfigArrayFactory._normalizeObjectConfigDataBody (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3199:25)
(node:2) ESLintRCWarning: You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.
(Use node --trace-warnings ... to show where the warning was created)

app/settings/page.tsx

Oops! Something went wrong! :(

ESLint: 9.39.1

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'configs' -> object with constructor 'Object'
| property 'flat' -> object with constructor 'Object'
| ...
| property 'plugins' -> object with constructor 'Object'
--- property 'react' closes the circle
Referenced from: /.eslintrc.json
at JSON.stringify ()
at /node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2255:45
at Array.map ()
at ConfigValidator.formatErrors (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2246:23)
at ConfigValidator.validateConfigSchema (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2277:84)
at ConfigArrayFactory._normalizeConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3123:19)
at ConfigArrayFactory._loadConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3088:21)
at ConfigArrayFactory._loadExtendedShareableConfig (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3393:21)
at ConfigArrayFactory._loadExtends (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3261:25)
at ConfigArrayFactory._normalizeObjectConfigDataBody (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3199:25)
(node:2) ESLintRCWarning: You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.
(Use node --trace-warnings ... to show where the warning was created)

hooks/useLoadingMessage.ts

Oops! Something went wrong! :(

ESLint: 9.39.1

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'configs' -> object with constructor 'Object'
| property 'flat' -> object with constructor 'Object'
| ...
| property 'plugins' -> object with constructor 'Object'
--- property 'react' closes the circle
Referenced from: /.eslintrc.json
at JSON.stringify ()
at /node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2255:45
at Array.map ()
at ConfigValidator.formatErrors (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2246:23)
at ConfigValidator.validateConfigSchema (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2277:84)
at ConfigArrayFactory._normalizeConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3123:19)
at ConfigArrayFactory._loadConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3088:21)
at ConfigArrayFactory._loadExtendedShareableConfig (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3393:21)
at ConfigArrayFactory._loadExtends (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3261:25)
at ConfigArrayFactory._normalizeObjectConfigDataBody (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3199:25)
(node:2) ESLintRCWarning: You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.
(Use node --trace-warnings ... to show where the warning was created)


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.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 4 issues, and left some high level feedback:

  • The new pattern in AssessmentContent performs sessionStorage reads/writes and state updates during render, which are side effects and can behave poorly with React Strict Mode; consider moving this logic back into a guarded useEffect or a custom hook that satisfies the lint rule without doing work in the render phase.
  • The previous useEffect for savedCandidates ran on mount to hydrate existing candidates from sessionStorage even when assessment/githubData were absent, whereas the new render-time block only runs when both are truthy; if this change is unintended, you may want a separate initialization path to preserve the original behaviour.
  • In AssessmentContent, the reuse and ordering of prevAssessmentRef between the completeness warning and savedCandidates logic means the second block will never treat assessment changes as a trigger in the same render; if assessment updates are expected to refresh saved candidates, consider using independent previous-value tracking or adjusting the control flow.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new pattern in `AssessmentContent` performs sessionStorage reads/writes and state updates during render, which are side effects and can behave poorly with React Strict Mode; consider moving this logic back into a guarded `useEffect` or a custom hook that satisfies the lint rule without doing work in the render phase.
- The previous `useEffect` for `savedCandidates` ran on mount to hydrate existing candidates from sessionStorage even when `assessment`/`githubData` were absent, whereas the new render-time block only runs when both are truthy; if this change is unintended, you may want a separate initialization path to preserve the original behaviour.
- In `AssessmentContent`, the reuse and ordering of `prevAssessmentRef` between the completeness warning and savedCandidates logic means the second block will never treat assessment changes as a trigger in the same render; if assessment updates are expected to refresh saved candidates, consider using independent previous-value tracking or adjusting the control flow.

## Individual Comments

### Comment 1
<location path="app/assessment/page.tsx" line_range="40-49" />
<code_context>
+  const prevAssessmentRef = useRef<AssessmentResult | null>(null);
+  const prevGithubDataRef = useRef<UserAssessmentData | null>(null);
+
+  // Derive showCompletenessWarning during render (not in an effect) to avoid
+  // ESLint react-hooks/set-state-in-effect.
+  if (assessment && assessment !== prevAssessmentRef.current) {
+    prevAssessmentRef.current = assessment;
+    const hasMissing = !assessment.summary || assessment.summary.length < 20 ||
+      !assessment.detailedReport || assessment.detailedReport.length < 200 ||
+      !assessment.repoAssessments || assessment.repoAssessments.length === 0 ||
+      !assessment.timeline || assessment.timeline.length === 0 ||
+      !assessment.swot.strengths || assessment.swot.strengths.length === 0;
+    if (hasMissing !== showCompletenessWarning) setShowCompletenessWarning(hasMissing);
+  }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid calling setShowCompletenessWarning during render; move this logic into an effect or memoized derivation.

Setting state during render breaks React’s expectation that renders are pure and can cause subtle issues, especially with concurrent rendering. Instead, derive `hasMissing` from `assessment` in a `useMemo` and update `showCompletenessWarning` in a `useEffect`, or compute the warning condition directly from `assessment` without keeping separate state.
</issue_to_address>

### Comment 2
<location path="app/assessment/page.tsx" line_range="52-61" />
<code_context>
+    if (hasMissing !== showCompletenessWarning) setShowCompletenessWarning(hasMissing);
+  }
+
+  // Derive savedCandidates during render (not in an effect).
+  if (assessment && githubData && (assessment !== prevAssessmentRef.current || githubData !== prevGithubDataRef.current)) {
+    prevGithubDataRef.current = githubData;
+    const stored: ComparisonCandidate[] = JSON.parse(sessionStorage.getItem('assessedCandidates') || '[]');
+    const entry: ComparisonCandidate = { username: githubData.username, avatarUrl: githubData.avatarUrl, assessment };
+    const idx = stored.findIndex((c: ComparisonCandidate) => c.username === githubData.username);
+    if (idx >= 0) stored[idx] = entry;
+    else stored.push(entry);
+    try { sessionStorage.setItem('assessedCandidates', JSON.stringify(stored)); } catch {}
+    if (stored.length !== savedCandidates.length || stored.some((c, i) => c.username !== savedCandidates[i]?.username)) {
+      setSavedCandidates(stored);
+    }
+  }
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid running sessionStorage reads/writes and setSavedCandidates in the render path.

This block introduces side effects (sessionStorage access and `setSavedCandidates`) during render, violating React’s requirement that render be pure and risking repeated storage writes and state updates on every render where the condition passes. Move this logic into an effect keyed on `assessment` and `githubData`, and use that effect to keep storage and `savedCandidates` in sync instead of updating them during render.
</issue_to_address>

### Comment 3
<location path="hooks/useLoadingMessage.ts" line_range="78-83" />
<code_context>
-  const [index, setIndex] = useState(0);
   const shuffled = useMemo(() => shuffle(messages), [messages]);
+  const [index, setIndex] = useState(0);
+  const [prevShuffled, setPrevShuffled] = useState(shuffled);

-  // Reset index when the message set changes.
-  useEffect(() => {
-    setIndex(0);
-  }, [shuffled]);
+  // Reset index when the message set changes (derived state, no effect needed).
+  if (shuffled !== prevShuffled) {
+    setPrevShuffled(shuffled);
+    if (index !== 0) setIndex(0);
+  }

</code_context>
<issue_to_address>
**issue (bug_risk):** Resetting index via setState in the body of the hook introduces impure render logic.

The `if (shuffled !== prevShuffled)` guard avoids an immediate loop, but calling `setPrevShuffled` and `setIndex` directly in the hook body still breaks React’s requirement that components/hooks be pure during render. This can conflict with concurrent rendering and lead to subtle bugs. Please move the reset logic into a `useEffect` keyed on `shuffled` so state updates happen outside the render phase while still resetting `index` appropriately.
</issue_to_address>

### Comment 4
<location path="app/settings/page.tsx" line_range="13-18" />
<code_context>
   const router = useRouter();
   const { settings, updateSettings } = useStore();
   const [localSettings, setLocalSettings] = useState(settings);
+  const [prevSettings, setPrevSettings] = useState(settings);

-  useEffect(() => {
+  // Sync localSettings when the store settings object is replaced externally.
+  if (settings !== prevSettings) {
+    setPrevSettings(settings);
     setLocalSettings(settings);
-  }, [settings]);
+  }
</code_context>
<issue_to_address>
**issue (bug_risk):** Synchronizing localSettings from settings via setState in render should be moved back into an effect.

This `if (settings !== prevSettings) { ... }` block runs during render and triggers state updates, which is incompatible with React’s concurrent rendering and can cause subtle, hard-to-debug issues. The sync logic should live in a `useEffect` keyed on `settings` so renders remain pure while `localSettings` stays aligned with the store when `settings` changes.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread app/assessment/page.tsx Outdated
Comment thread app/assessment/page.tsx Outdated
Comment thread hooks/useLoadingMessage.ts Outdated
Comment thread app/settings/page.tsx Outdated
@Yuvraj-Sarathe Yuvraj-Sarathe added bug Something isn't working backend Deals with backend problems ECSoC26 Elite Coders Summer of Camp merged ECSoC26-L3 level: advanced good-backend CI/CD labels Jul 25, 2026
@Yuvraj-Sarathe

Copy link
Copy Markdown
Owner

@Yuva-Deekshitha-N resolve these suggestions and comments from Sourcery and you are ready to go!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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.

Inline comments:
In `@app/assessment/page.tsx`:
- Around line 50-56: Update the sessionStorage handling in the assessment effect
around the stored candidate array: move storage reads and JSON parsing inside
the existing try/catch, validate that the parsed value is an array of valid
ComparisonCandidate entries before using it, and fall back to [] for unavailable
storage, malformed JSON, or invalid shapes. Preserve the existing
replacement/push behavior and ensure setSavedCandidates still receives the safe
fallback or validated array.
- Around line 40-45: The assessment page’s local hasMissing predicate duplicates
incomplete and inconsistent criteria. Reuse the canonical completeness validator
from lib/ai.ts in the assessment flow, passing the full assessment and using its
result for setShowCompletenessWarning; remove the partial field checks so the UI
applies all eight criteria and the required issue threshold.

In `@app/settings/page.tsx`:
- Around line 14-17: Update the settings synchronization in the page component
so `localSettings` is reconciled during render when the `settings` object
identity changes, tracking the previous `settings` value to avoid unnecessary
updates. Remove the `useEffect` import and effect once unused, while preserving
synchronization for externally replaced settings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f7af396-f5d1-4bb0-8672-20cd08c0ff48

📥 Commits

Reviewing files that changed from the base of the PR and between 89d90ff and 8af3d73.

📒 Files selected for processing (3)
  • app/assessment/page.tsx
  • app/settings/page.tsx
  • hooks/useLoadingMessage.ts

Comment thread app/assessment/page.tsx Outdated
Comment on lines +40 to +45
const hasMissing = !assessment.summary || assessment.summary.length < 20 ||
!assessment.detailedReport || assessment.detailedReport.length < 200 ||
!assessment.repoAssessments || assessment.repoAssessments.length === 0 ||
!assessment.timeline || assessment.timeline.length === 0 ||
!assessment.swot.strengths || assessment.swot.strengths.length === 0;
setShowCompletenessWarning(hasMissing);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reuse the canonical completeness criteria.

This predicate checks only five fields and warns when any one is missing, while lib/ai.ts Lines 264-279 checks eight fields and considers the assessment incomplete only after at least three issues. The UI can therefore show false warnings or miss incomplete assessments. Share the canonical validator instead of duplicating a partial predicate.

🤖 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 `@app/assessment/page.tsx` around lines 40 - 45, The assessment page’s local
hasMissing predicate duplicates incomplete and inconsistent criteria. Reuse the
canonical completeness validator from lib/ai.ts in the assessment flow, passing
the full assessment and using its result for setShowCompletenessWarning; remove
the partial field checks so the UI applies all eight criteria and the required
issue threshold.

Comment thread app/assessment/page.tsx Outdated
Comment on lines +50 to +56
const stored: ComparisonCandidate[] = JSON.parse(sessionStorage.getItem('assessedCandidates') || '[]');
const entry: ComparisonCandidate = { username: githubData.username, avatarUrl: githubData.avatarUrl, assessment };
const idx = stored.findIndex((c: ComparisonCandidate) => c.username === githubData.username);
if (idx >= 0) stored[idx] = entry;
else stored.push(entry);
try { sessionStorage.setItem('assessedCandidates', JSON.stringify(stored)); } catch {}
setSavedCandidates(stored);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard sessionStorage reads and validate the stored shape.

getItem and JSON.parse are outside the try block, and non-array or malformed data is used as ComparisonCandidate[] without validation. A corrupted/stale value or unavailable storage can abort this effect before savedCandidates is updated. Catch read/parse failures and accept only validated candidate arrays, falling back to [].

🤖 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 `@app/assessment/page.tsx` around lines 50 - 56, Update the sessionStorage
handling in the assessment effect around the stored candidate array: move
storage reads and JSON parsing inside the existing try/catch, validate that the
parsed value is an array of valid ComparisonCandidate entries before using it,
and fall back to [] for unavailable storage, malformed JSON, or invalid shapes.
Preserve the existing replacement/push behavior and ensure setSavedCandidates
still receives the safe fallback or validated array.

Comment thread app/settings/page.tsx Outdated
Comment on lines 14 to 17
// Sync localSettings when the store settings object is replaced externally.
useEffect(() => {
setLocalSettings(settings);
}, [settings]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repo files matching app/settings/page.tsx:"
fd -a 'page\.tsx$' . | sed 's#^\./##' | grep 'app/settings/page.tsx' || true

if [ -f app/settings/page.tsx ]; then
  echo
  echo "app/settings/page.tsx outline:"
  ast-grep outline app/settings/page.tsx || true
  echo
  echo "app/settings/page.tsx relevant content:"
  nl -ba app/settings/page.tsx | sed -n '1,120p'
fi

echo
echo "Search for eslint config/plugins/rules and set-state-in-effect usages:"
rg -n "react-hooks|eslint-plugin-react-hooks|set-state-in-effect|no-set-state-in-effect|eslintConfig|eslint" -S --glob '!node_modules' --glob '!dist' --glob '!build' . | sed -n '1,160p'

Repository: Yuvraj-Sarathe/GitDeep

Length of output: 424


🌐 Web query:

React hooks eslint set-state-in-effect lint rules state in effect synchronous call allowed?

💡 Result:

Yes, it is generally allowed to call a state setter function (setState) within a useEffect hook [1][2], provided it is done correctly to avoid issues like infinite loops [1]. However, there is a specific ESLint rule, react-hooks/set-state-in-effect, which validates against calling setState synchronously inside an effect [3]. Key considerations regarding this practice: 1. Performance Concerns: The react-hooks/set-state-in-effect rule warns against synchronous setState calls because they can trigger unnecessary re-renders, degrade performance, and cause visual jank [3]. When you update state in an effect, React must re-render the component, re-apply changes, and re-run effects, creating an extra render pass [3]. 2. Alternatives: If the state update is based on existing props or state, it is often better to calculate that value during the render phase rather than using useEffect to update it later [3]. This simplifies the component and avoids the extra render cycle [3]. 3. Avoiding Infinite Loops: When you do use setState in an effect, ensure you have a proper dependency array [1]. If you update state based on its previous value, use the functional update form (e.g., setCounter(prev => prev + 1)) to avoid needing the state variable itself in the dependency array [4][1][2]. 4. Dependency Stability: You do not need to include the setState setter function itself in the useEffect dependency array, as React guarantees its identity is stable across renders [5][2]. 5. Linting Rules: - react-hooks/set-state-in-effect: Focuses on the performance impact of synchronous updates within effects [3][6]. - react-hooks/exhaustive-deps: Ensures that all reactive values used inside the effect are included in the dependency array to prevent stale closures [7][8]. In summary, while technically possible, you should aim to minimize synchronous state updates within effects by deriving data during render whenever possible [3]. If an update is necessary, ensure it is carefully guarded or triggered by appropriate dependencies to prevent infinite re-render loops [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "app/settings/page.tsx content:"
awk '{printf "%6d  %s\n", NR, $0}' app/settings/page.tsx

echo
echo "All eslint config/package mentions:"
for f in package.json .eslintrc .eslintrc.js .eslintrc.cjs eslint.config.js eslint.config.mjs eslint.config.cjs; do
  [ -f "$f" ] && { echo "--- $f"; awk '{printf "%6d  %s\n", NR, $0}' "$f"; }
done

echo
echo "Search react-hooks rules:"
rg -n "react-hooks|set-state-in-effect|setState|setLocalSettings|settings" -S --glob '!node_modules' --glob '!dist' --glob '!build' . | sed -n '1,220p'

Repository: Yuvraj-Sarathe/GitDeep

Length of output: 50379


Move the localSettings sync out of useEffect.

setLocalSettings(settings) inside useEffect still triggers the react-hooks/set-state-in-effect lint from eslint-config-next; the comment on the effect does not suppress the rule. Track the previous settings value during render and update localSettings there, then remove useEffect if it is no longer needed.

🤖 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 `@app/settings/page.tsx` around lines 14 - 17, Update the settings
synchronization in the page component so `localSettings` is reconciled during
render when the `settings` object identity changes, tracking the previous
`settings` value to avoid unnecessary updates. Remove the `useEffect` import and
effect once unused, while preserving synchronization for externally replaced
settings.

@Yuvraj-Sarathe
Yuvraj-Sarathe merged commit 0452800 into Yuvraj-Sarathe:main Jul 26, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Deals with backend problems bug Something isn't working CI/CD ECSoC26-L3 ECSoC26 Elite Coders Summer of Camp good-backend level: advanced merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Solve the current PR Verification CI / Run Linter and Tests (pull_request)

2 participants