Skip to content

Fix "Maximum update depth exceeded" in auth flow components (#3697)#17

Open
webdevelopersrinu wants to merge 4 commits into
thunder-id:mainfrom
webdevelopersrinu:fix/3697-maximum-update-depth
Open

Fix "Maximum update depth exceeded" in auth flow components (#3697)#17
webdevelopersrinu wants to merge 4 commits into
thunder-id:mainfrom
webdevelopersrinu:fix/3697-maximum-update-depth

Conversation

@webdevelopersrinu

@webdevelopersrinu webdevelopersrinu commented Jul 6, 2026

Copy link
Copy Markdown

Summary

Fixes thunder-id/thunderid#3697@thunderid/react throws a continuous "Maximum update depth exceeded" error during flow rendering.

Root cause

In BaseSignIn, formFields was rebuilt as a new array on every render and passed to useForm({ fields }). That new identity cascades through the hook:

fieldsgetFieldConfig ([fields]) → validateFieldsetTouched

so setTouched gets a new identity every render. setTouched is a dependency of the useEffect that projects server errors, and that effect calls clearFormErrors() unconditionally (which does setFormErrors({}), a fresh object). The result is an infinite loop:

render → effect fires → clearFormErrors() → re-render → new formFields → new setTouched → effect fires again → …

Fix

Memoize the derived field list so its identity is stable across renders:

const formFields = useMemo(
  () => (components ? extractFormFields(components) : []),
  [components, extractFormFields],
);

The same unmemoized pattern existed in BaseSignUp and BaseRecovery, so all three are fixed consistently.

Changes

  • BaseSignIn.tsx — memoize formFields
  • BaseSignUp.tsx — memoize formFields
  • BaseRecovery.tsx — memoize formFields

Verification

  • tsc typecheck passes clean
  • ✅ No new lint issues; no react-hooks/exhaustive-deps warnings
  • ✅ Verified the flow no longer loops in a running app

Notes

useMemo returns the identical array contents as before — only the reference identity is now stable — so there is no behavioral change beyond eliminating the render loop.

Summary by CodeRabbit

  • Bug Fixes
    • Improved sign-in, sign-up, and recovery experiences by keeping the form field setup consistent across renders.
    • Reduced unnecessary updates to form state, improving performance and helping avoid potential repeated update behavior.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 51b651e6-47f6-42ef-8b80-4ffdd39d30e6

📥 Commits

Reviewing files that changed from the base of the PR and between 8a1851e and 72d97b1.

📒 Files selected for processing (1)
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx

📝 Walkthrough

Walkthrough

The auth presentation components now memoize their derived formFields arrays with useMemo, and BaseSignIn also uses a shared empty-components default so the derived array keeps a stable identity across renders.

Changes

Memoize formFields across auth components

Layer / File(s) Summary
Memoize formFields in BaseRecovery
packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx
Adds useMemo import and wraps formFields derivation from currentFlow.data.components via extractFormFields in useMemo with [currentFlow, extractFormFields] dependencies.
Memoize formFields in BaseSignIn
packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx
Adds useMemo, introduces EMPTY_COMPONENTS as the default components value, and wraps formFields derivation from components via extractFormFields in useMemo.
Memoize formFields in BaseSignUp
packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx
Adds useMemo and wraps formFields derivation from currentFlow via extractFormFields in useMemo, with comments about keeping identity stable.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main fix: preventing the auth flow update loop by addressing the maximum update depth error.
Description check ✅ Passed The description covers the problem, root cause, fix, changed files, and verification, so it is mostly complete despite not using the exact template headings.
Linked Issues check ✅ Passed The changes memoize form field arrays in the three auth components, which matches the linked issue's goal of stopping the continuous update loop.
Out of Scope Changes check ✅ Passed All changed files are directly tied to the reported auth flow loop fix; no unrelated or extra scope changes are evident.
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

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx (1)

232-235: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Static analysis flags unsafe-any on this memo (pre-existing typing gap).

ESLint reports unsafe assignment/member-access/call on currentFlow?.data?.components. This stems from currentFlow's loosely-typed .data shape rather than the new useMemo wrapper itself, but it's worth tightening the type of EmbeddedRecoveryFlowResponse.data (or narrowing with a type guard) to eliminate the any leakage, especially since the PR verification notes claim no new lint issues.

🤖 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 `@packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx`
around lines 232 - 235, The unsafe-any warning comes from the loosely typed
recovery flow payload used by BaseRecovery’s useMemo, not from the memo itself.
Tighten the type of EmbeddedRecoveryFlowResponse.data so
currentFlow.data.components is strongly typed, or add an explicit type
guard/narrowing before calling extractFormFields in BaseRecovery, and ensure the
currentFlow access path no longer leaks any into the memo.

Source: Linters/SAST tools

packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx (2)

461-465: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Static analysis unsafe-any flags on the touched lines.

Same as BaseRecovery, ESLint flags unsafe assignment/member-access due to currentFlow's loosely-typed .data. Pre-existing typing gap, not newly introduced by this refactor's logic, but worth tightening the response type to remove the any casts long-term.

🤖 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 `@packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx` around
lines 461 - 465, The memoized form field extraction in BaseSignUp is still
relying on unsafe any casts against currentFlow.data, which triggers
static-analysis unsafe-assignment/member-access flags. Tighten the typing of the
flow response/data shape used in the useMemo block and extractFormFields call so
the components property can be accessed without any assertions, following the
same approach used in BaseRecovery.

Source: Linters/SAST tools


458-465: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Memoization logic is correct; minor style nit on the non-null assertion.

currentFlow is state, so the useMemo dependency is stable across renders like in BaseRecovery. The expression itself is a bit convoluted — currentFlow!.data as any after already null-checking via currentFlow?.data as any. BaseRecovery's equivalent (currentFlow?.data?.components ? extractFormFields(currentFlow.data.components) : []) is cleaner and avoids the non-null assertion; consider aligning both implementations for consistency.

♻️ Suggested cleanup
   const formFields: FormField[] = useMemo(
-    () =>
-      (currentFlow?.data as any)?.components ? extractFormFields((currentFlow!.data as any).components) : [],
+    () => (currentFlow?.data as any)?.components ? extractFormFields((currentFlow?.data as any).components) : [],
     [currentFlow, extractFormFields],
   );
🤖 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 `@packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx` around
lines 458 - 465, The memoization logic in `BaseSignUp` is fine, but the
`formFields` derivation uses a redundant non-null assertion (`currentFlow!.data
as any`) after already guarding with optional chaining. Update the `useMemo`
expression in `BaseSignUp` to match the cleaner pattern used in `BaseRecovery`,
keeping the same behavior while removing the unnecessary assertion and improving
consistency.
🤖 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 `@packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx`:
- Around line 354-362: The `formFields` memo in `BaseSignIn` still depends on an
inline empty array when `components` is missing, which creates a new reference
on every render and breaks the stability fix. Hoist a shared empty-array
sentinel (for example a module-level constant) and use it in the `components`
fallback so `useMemo` can preserve identity across renders. Update the
`BaseSignIn` logic where `components`, `extractFormFields`, and `formFields` are
derived.

---

Nitpick comments:
In `@packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx`:
- Around line 232-235: The unsafe-any warning comes from the loosely typed
recovery flow payload used by BaseRecovery’s useMemo, not from the memo itself.
Tighten the type of EmbeddedRecoveryFlowResponse.data so
currentFlow.data.components is strongly typed, or add an explicit type
guard/narrowing before calling extractFormFields in BaseRecovery, and ensure the
currentFlow access path no longer leaks any into the memo.

In `@packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx`:
- Around line 461-465: The memoized form field extraction in BaseSignUp is still
relying on unsafe any casts against currentFlow.data, which triggers
static-analysis unsafe-assignment/member-access flags. Tighten the typing of the
flow response/data shape used in the useMemo block and extractFormFields call so
the components property can be accessed without any assertions, following the
same approach used in BaseRecovery.
- Around line 458-465: The memoization logic in `BaseSignUp` is fine, but the
`formFields` derivation uses a redundant non-null assertion (`currentFlow!.data
as any`) after already guarding with optional chaining. Update the `useMemo`
expression in `BaseSignUp` to match the cleaner pattern used in `BaseRecovery`,
keeping the same behavior while removing the unnecessary assertion and improving
consistency.
🪄 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: 133c9187-2306-40a7-863c-4d9a56c0819b

📥 Commits

Reviewing files that changed from the base of the PR and between a50ef7a and 4acac09.

📒 Files selected for processing (3)
  • packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx

Comment thread packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx (1)

349-352: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant truthiness check now that components defaults to EMPTY_COMPONENTS.

Since components always resolves to at least EMPTY_COMPONENTS (never undefined/null), the components ? ... : [] ternary's false branch is unreachable dead code.

♻️ Simplify
   const formFields: FormField[] = useMemo(
-    () => (components ? extractFormFields(components) : []),
+    () => extractFormFields(components),
     [components, extractFormFields],
   );
🤖 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 `@packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx` around
lines 349 - 352, In BaseSignIn’s useMemo for formFields, remove the redundant
`components ? ... : []` guard because `components` always falls back to
`EMPTY_COMPONENTS`; simplify the expression to always derive fields from
`extractFormFields(components)`. Keep the dependency list unchanged and ensure
the `formFields` initialization in `BaseSignIn` still uses the existing
`components` and `extractFormFields` references.
🤖 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.

Nitpick comments:
In `@packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx`:
- Around line 349-352: In BaseSignIn’s useMemo for formFields, remove the
redundant `components ? ... : []` guard because `components` always falls back
to `EMPTY_COMPONENTS`; simplify the expression to always derive fields from
`extractFormFields(components)`. Keep the dependency list unchanged and ensure
the `formFields` initialization in `BaseSignIn` still uses the existing
`components` and `extractFormFields` references.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 506e4aae-3422-4275-a044-5cdb99772d83

📥 Commits

Reviewing files that changed from the base of the PR and between 4acac09 and 8a1851e.

📒 Files selected for processing (2)
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx

@brionmario

brionmario commented Jul 9, 2026

Copy link
Copy Markdown
Member

Hey @webdevelopersrinu!

Thanks a lot for the PR. Really appreciate the effor.

Could you kindly share a couple of screenshots to validate the fix.

  • A screenshot showing the stack exceed error before your fixes.
  • A screenshot showing the fixed version

Cheers! 🙌

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.

Maximum update depth exceeded issue, while using thunder react sdk

2 participants