Skip to content

fix: add Suspense boundaries to prevent React #426 during i18n loading - #2654

Merged
serikjensen merged 12 commits into
mainfrom
claude/employee-list-suspense-boundary-cv2qdm
Aug 27, 2026
Merged

fix: add Suspense boundaries to prevent React #426 during i18n loading#2654
serikjensen merged 12 commits into
mainfrom
claude/employee-list-suspense-boundary-cv2qdm

Conversation

@serikjensen

@serikjensen serikjensen commented Aug 26, 2026

Copy link
Copy Markdown
Member

Summary

Fixes React #426 errors caused by useI18n() and useTranslation() suspending without a Suspense boundary during synchronous state-machine transitions. The core fix adds a Suspense boundary around step components in Flow, which systematically protects all contextual wrappers rendered as Flow steps. Two standalone components that aren't rendered by Flow also get their own BaseBoundaries wrappers.

Changes

  • Flow.tsx: Wrap the active step component in <Suspense fallback={<LoadingIndicator />}>, matching the existing pattern used for FlowHeader
  • InformationRequests.tsx: Add BaseBoundaries wrapper (standalone component not rendered by Flow, had no outer Suspense boundary)
  • OffCycleDeductionsSetting.tsx: Add BaseBoundaries wrapper (standalone public component, had no outer Suspense boundary)

Testing

  • npx tsc --noEmit passes
  • npm run test -- --run — 347 passed, 2 flaky timeouts in unrelated PaymentFlow.test.tsx
  • npm run test -- --run src/components/Flow/Flow.test.tsx
Screen.Recording.2026-08-27.at.9.25.07.AM.mov
Screen.Recording.2026-08-27.at.9.27.16.AM.mov
Screen.Recording.2026-08-27.at.9.30.06.AM.mov

Generated by Claude Code

claude and others added 10 commits August 21, 2026 20:01
Several components called useI18n() above their nearest Suspense
boundary. When a user navigated to these components via a synchronous
state-machine transition and the i18n namespace was not yet cached,
useI18n suspended above the boundary and React threw error #426.

The fix follows the same pattern as #2554: move useI18n into an inner
component rendered inside BaseBoundaries so the suspension is caught
by the component's own Suspense boundary.

Affected components:
- Employee Dashboard contextual wrapper (DashboardViewContextual)
- Contractor Dashboard contextual wrapper (DashboardViewContextual)
- TerminateEmployee/TerminationSummary contextual wrappers
- InitialEditCompensation/EditCompensation contextual wrappers
- PayrollLandingTabs contextual wrapper (also has suspense queries)
- InformationRequestsFlow (standalone, no boundary at all)
- OffCycleDeductionsSetting (leaf component, no boundary)
…TerminationSummary

Instead of wrapping contextual wrappers in BaseBoundaries, remove the
suspending useI18n calls entirely — the underlying components (Dashboard,
EditCompensation, TerminateEmployee) already load their namespaces inside
their own boundaries.

For TerminationSummary, the cancelSuccess translation is moved into the
component itself so the contextual wrapper no longer needs useI18n or
event interception.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDj8FTh4FwRJfJJGdsZF1j
useTranslation also suspends by default (useSuspense: true in
react-i18next v17), so the previous fix of only removing useI18n
was insufficient. This moves all translation-dependent logic into
the underlying components where it runs inside BaseBoundaries:

- Dashboard (Employee/Contractor): add successAlert prop, render
  alert inside DashboardRoot (inside BaseBoundaries)
- EditCompensation: make title/submitCtaLabel optional, add mode
  prop to select defaults computed inside Root (inside BaseBoundaries)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDj8FTh4FwRJfJJGdsZF1j
#426

Wraps the active step component in Flow.tsx with <Suspense> so that
useI18n/useTranslation calls in contextual wrappers don't throw React
#426 during synchronous state-machine transitions.

Reverts earlier approach that moved translations/alerts into public
component APIs (Dashboard, EditCompensation) — the Flow-level boundary
is the systematic fix that protects all contextual wrappers without
changing public interfaces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDj8FTh4FwRJfJJGdsZF1j
…llback

Revert changes to TerminationFlowComponents, TerminationSummary, and
PayrollLandingFlowComponents that are now redundant with the Flow-level
Suspense boundary. Use the SDK loading indicator as the step fallback
instead of null.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDj8FTh4FwRJfJJGdsZF1j
@serikjensen
serikjensen requested a review from a team as a code owner August 26, 2026 22:50
@serikjensen
serikjensen enabled auto-merge August 27, 2026 18:23
@serikjensen
serikjensen added this pull request to the merge queue Aug 27, 2026
Merged via the queue into main with commit 94c4287 Aug 27, 2026
35 checks passed
@serikjensen
serikjensen deleted the claude/employee-list-suspense-boundary-cv2qdm branch August 27, 2026 18:46
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.

3 participants