#1

React Review Audit

Openreact-doctor[bot]opened 6mo ago

Score: 90/100 · 1 error · 25 warnings

Copy as prompt
Fix the following React Review diagnostics in my codebase.

## Errors (1)

1. [error] effect-needs-cleanup — hooks/use-wizard-game.ts:73
   useEffect schedules `setTimeout(...)` but never returns a cleanup — leaks the registration on every re-run and on unmount. Return a cleanup function that calls clearTimeout(...)

## Warnings (25)

2. [warning] label-has-associated-control — components/game/setup-screen.tsx:31
   A form label must be associated with a control.

3. [warning] label-has-associated-control — components/game/setup-screen.tsx:55
   A form label must be associated with a control.

4. [warning] label-has-associated-control — components/game/setup-screen.tsx:68
   A form label must be associated with a control.

5. [warning] design-no-bold-heading — components/game/setup-screen.tsx:22
   font-bold on <h1> crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)

6. [warning] no-array-index-as-key — components/game/setup-screen.tsx:74
   Array index "i" used as key — causes bugs when list is reordered or filtered

7. [warning] design-no-bold-heading — components/game/mode-select.tsx:14
   font-bold on <h1> crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)

8. [warning] label-has-associated-control — components/game/lobby-screen.tsx:60
   A form label must be associated with a control.

9. [warning] label-has-associated-control — components/game/lobby-screen.tsx:75
   A form label must be associated with a control.

10. [warning] label-has-associated-control — components/game/lobby-screen.tsx:112
   A form label must be associated with a control.

11. [warning] design-no-bold-heading — components/game/lobby-screen.tsx:30
   font-bold on <h1> crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)

12. [warning] design-no-bold-heading — components/game/lobby-screen.tsx:169
   font-bold on <h2> crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)

13. [warning] no-array-index-as-key — components/game/lobby-screen.tsx:229
   Array index "i" used as key — causes bugs when list is reordered or filtered

14. [warning] design-no-three-period-ellipsis — components/game/lobby-screen.tsx:235
   Three-period ellipsis ("...") in JSX text — use the actual ellipsis character "…" (or `&hellip;`)

15. [warning] design-no-three-period-ellipsis — components/game/lobby-screen.tsx:260
   Three-period ellipsis ("...") in JSX text — use the actual ellipsis character "…" (or `&hellip;`)

16. [warning] design-no-bold-heading — components/game/round-result-overlay.tsx:20
   font-bold on <h2> crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)

17. [warning] js-tosorted-immutable — lib/wizard/ai.ts:69
   [...array].sort() — use array.toSorted() for immutable sorting (ES2023)

18. [warning] js-cache-property-access — lib/wizard/ai.ts:170
   play.card.rank is read 4 times inside this loop — hoist into a const at the top of the loop body

19. [warning] rerender-lazy-state-init — hooks/use-multiplayer-game.ts:30
   useState(createInitialState()) calls initializer on every render — use useState(() => createInitialState()) for lazy initialization

20. [warning] no-cascading-set-state — hooks/use-multiplayer-game.ts:361
   4 setState calls in a single useEffect — consider using useReducer or deriving state

21. [warning] design-no-bold-heading — components/game/game-over-screen.tsx:19
   font-bold on <h1> crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)

22. [warning] no-static-element-interactions — components/game/card.tsx:60
   Static HTML elements with event handlers require a role.

23. [warning] no-static-element-interactions — components/game/card.tsx:115
   Static HTML elements with event handlers require a role.

24. [warning] no-cascading-set-state — hooks/use-wizard-game.ts:73
   9 setState calls in a single useEffect — consider using useReducer or deriving state

25. [warning] nextjs-missing-metadata — app/page.tsx:1
   Page without metadata or generateMetadata export — hurts SEO

26. [warning] rerender-state-only-in-handlers — app/page.tsx:34
   useState "mode" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

❌ Errors (1)

effect-needs-cleanup

useEffect schedules setTimeout(...) but never returns a cleanup — leaks the registration on every re-run and on unmount. Return a cleanup function that calls clearTimeout(...)

Return a cleanup function that releases the subscription / timer: return () =&gt; target.removeEventListener(name, handler) for listeners, return () =&gt; clearInterval(id) / clearTimeout(id) for timers, or return unsubscribe if the subscribe call already returned one

hooks/use-wizard-game.ts:73


⚠️ Warnings (25)

label-has-associated-control

A form label must be associated with a control.

Either give the label a htmlFor attribute with the id of the associated control, or wrap the label around the control.

components/game/setup-screen.tsx:31
components/game/setup-screen.tsx:55
components/game/setup-screen.tsx:68
components/game/lobby-screen.tsx:60
components/game/lobby-screen.tsx:75
components/game/lobby-screen.tsx:112

design-no-bold-heading

font-bold on <h1> crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)

Use font-semibold (600) or font-medium (500) on headings — 700+ crushes letter counter shapes at display sizes

components/game/setup-screen.tsx:22
components/game/mode-select.tsx:14
components/game/lobby-screen.tsx:30
components/game/lobby-screen.tsx:169
components/game/round-result-overlay.tsx:20
components/game/game-over-screen.tsx:19

no-array-index-as-key

Array index "i" used as key — causes bugs when list is reordered or filtered

Use a stable unique identifier: key={item.id} or key={item.slug} — index keys break on reorder/filter

components/game/setup-screen.tsx:74
components/game/lobby-screen.tsx:229

design-no-three-period-ellipsis

Three-period ellipsis ("...") in JSX text — use the actual ellipsis character "…" (or &hellip;)

Use the typographic ellipsis "…" (or &hellip;) instead of three periods — pairs with action-with-followup labels ("Rename…", "Loading…")

components/game/lobby-screen.tsx:235
components/game/lobby-screen.tsx:260

no-cascading-set-state

4 setState calls in a single useEffect — consider using useReducer or deriving state

Combine into useReducer: const [state, dispatch] = useReducer(reducer, initialState)

hooks/use-multiplayer-game.ts:361
hooks/use-wizard-game.ts:73

no-static-element-interactions

Static HTML elements with event handlers require a role.

Add a role attribute to this element, or use a semantic HTML element instead.

components/game/card.tsx:60
components/game/card.tsx:115

js-tosorted-immutable

[...array].sort() — use array.toSorted() for immutable sorting (ES2023)

Use array.toSorted() (ES2023) instead of [...array].sort() for immutable sorting without the spread allocation

lib/wizard/ai.ts:69

js-cache-property-access

play.card.rank is read 4 times inside this loop — hoist into a const at the top of the loop body

Hoist the deep member access into a const at the top of the loop body: const { x, y } = obj.deeply.nested

lib/wizard/ai.ts:170

rerender-lazy-state-init

useState(createInitialState()) calls initializer on every render — use useState(() => createInitialState()) for lazy initialization

Wrap in an arrow function so it only runs once: useState(() =&gt; expensiveComputation())

hooks/use-multiplayer-game.ts:30

nextjs-missing-metadata

Page without metadata or generateMetadata export — hurts SEO

Add export const metadata = { title: '...', description: '...' } or export async function generateMetadata()

app/page.tsx:1

rerender-state-only-in-handlers

useState "mode" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

Replace useState with useRef when the value is only mutated and never read in render — ref.current = ... updates without re-rendering the component

app/page.tsx:34


Last scored May 14, 2026 at 7:35 AM UTC. Maintained by React Review.

Activity

0

No activity yet.