"Climb the Spire causes React crash (Minified React error #185)" #229

Closed
opened 2026-05-31 10:17:51 +02:00 by Anexim · 2 comments
Owner

Description: Clicking "Climb the Spire" crashes the game with Minified React error #185 ("Maximum update depth exceeded").

Root Cause Investigation:

The enterSpireMode action in combatStore.ts sets currentAction: 'climb' and spireMode: true. This triggers page.tsx to render <SpireCombatPage /> inside an <ErrorBoundary>.

The SpireCombatPage component uses useSpireStats() which calls computeDisciplineEffects(). This function accesses useDisciplineStore.getState() and iterates all disciplines. If discipline state is somehow corrupted or triggers re-renders during render (e.g., re-computing discipline effects triggers state updates), it can cause an infinite render loop.

Additionally, the useMemo calls inside the component depend on equippedInstances and equipmentInstances from useCraftingStore, which are accessed outside of useShallow (as bare selectors), potentially causing unnecessary re-renders.

Files involved:

  • src/app/page.tsx — conditional render of SpireCombatPage
  • src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx — the spire combat page
  • src/lib/game/stores/combatStore.tsenterSpireMode action

Likely triggers:

  1. computeDisciplineEffects() called during render may cause cascading state reads
  2. generateSpireFloorState uses Math.random() during render inside useMemo, causing referential instability
  3. The totalRooms and useEffect calling setCurrentRoom can trigger render loops when currentFloor is a guardian floor (getRoomsForFloor returns 1, but the effect re-runs)

Repro: Start a game, click "Climb the Spire" button. React error #185 appears immediately.

AGENTS.md rules: All fixes must keep files under 400 lines.

**Description:** Clicking "Climb the Spire" crashes the game with Minified React error #185 ("Maximum update depth exceeded"). **Root Cause Investigation:** The `enterSpireMode` action in `combatStore.ts` sets `currentAction: 'climb'` and `spireMode: true`. This triggers `page.tsx` to render `<SpireCombatPage />` inside an `<ErrorBoundary>`. The `SpireCombatPage` component uses `useSpireStats()` which calls `computeDisciplineEffects()`. This function accesses `useDisciplineStore.getState()` and iterates all disciplines. If discipline state is somehow corrupted or triggers re-renders during render (e.g., re-computing discipline effects triggers state updates), it can cause an infinite render loop. Additionally, the `useMemo` calls inside the component depend on `equippedInstances` and `equipmentInstances` from `useCraftingStore`, which are accessed outside of `useShallow` (as bare selectors), potentially causing unnecessary re-renders. **Files involved:** - `src/app/page.tsx` — conditional render of SpireCombatPage - `src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx` — the spire combat page - `src/lib/game/stores/combatStore.ts` — `enterSpireMode` action **Likely triggers:** 1. `computeDisciplineEffects()` called during render may cause cascading state reads 2. `generateSpireFloorState` uses `Math.random()` during render inside `useMemo`, causing referential instability 3. The `totalRooms` and `useEffect` calling `setCurrentRoom` can trigger render loops when `currentFloor` is a guardian floor (getRoomsForFloor returns 1, but the effect re-runs) **Repro:** Start a game, click "Climb the Spire" button. React error #185 appears immediately. **AGENTS.md rules:** All fixes must keep files under 400 lines.
Anexim added the ai:todo label 2026-05-31 10:17:51 +02:00
n8n-gitea was assigned by Anexim 2026-05-31 10:17:51 +02:00
Author
Owner

Starting work on the Spire crash fix (React error #185 - Maximum update depth exceeded). Investigating the root cause in SpireCombatPage.tsx, page.tsx, and combatStore.ts.

Starting work on the Spire crash fix (React error #185 - Maximum update depth exceeded). Investigating the root cause in SpireCombatPage.tsx, page.tsx, and combatStore.ts.
Author
Owner

Fixed React crash #185 (infinite render loop) when entering Spire mode. Two root causes addressed:\n1. getRoomsForFloor() used Math.random() making totalRooms unstable — replaced with deterministic seeded random in component\n2. useCraftingStore selectors for equippedInstances/equipmentInstances lacked useShallow — wrapped in useShallow to prevent unnecessary re-renders\n\nAll 36 spire-utils tests pass.

Fixed React crash #185 (infinite render loop) when entering Spire mode. Two root causes addressed:\n1. getRoomsForFloor() used Math.random() making totalRooms unstable — replaced with deterministic seeded random in component\n2. useCraftingStore selectors for equippedInstances/equipmentInstances lacked useShallow — wrapped in useShallow to prevent unnecessary re-renders\n\nAll 36 spire-utils tests pass.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Anexim/Mana-Loop#229