Skip to content

React Review Audit #8

@reactreview

Description

@reactreview

Score: 83/100 · 1 error · 43 warnings

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

## Errors (1)

1. [error] effect-needs-cleanup — apps/terminal/src/components/terminal.tsx:221
   useEffect subscribes via `addEventListener(...)` but never returns a cleanup - leaks the registration on every re-run and on unmount. Return a cleanup function that calls the matching remove/unsubscribe call

## Warnings (43)

2. [warning] unused-type-export — apps/terminal/src/lib/errors.ts:52
   The exported symbol "WebErrorCode" is not referenced by reachable modules.

3. [warning] unused-type-export — apps/terminal/src/lib/errors.ts:53
   The exported symbol "WebErrorKind" is not referenced by reachable modules.

4. [warning] unused-type-export — apps/terminal/src/lib/local-fonts-types.ts:1
   The exported symbol "LocalFontData" is not referenced by reachable modules.

5. [warning] unused-export — apps/terminal/src/lib/schemas.ts:2
   The exported symbol "clientToServerMessageSchema" is not referenced by reachable modules.

6. [warning] unused-export — apps/terminal/src/lib/terminal-fonts.ts:18
   The exported symbol "buildLocalTerminalFont" is not referenced by reachable modules.

7. [warning] unused-type-export — apps/terminal/src/lib/terminal-themes.ts:3
   The exported symbol "TerminalTheme" is not referenced by reachable modules.

8. [warning] unused-type-export — apps/terminal/src/utils/restore-terminal-scroll-anchor.ts:6
   The exported symbol "TerminalScrollAnchorRestorer" is not referenced by reachable modules.

9. [warning] unused-file — apps/terminal/tests/setup.ts:0
   This source file is not reachable from any package, framework, test, or support entrypoint.

10. [warning] unlisted-dependency — apps/terminal/package.json:0
   "@types/vite/client" is referenced by tsconfig.json but not listed in the workspace package.json.

11. [warning] unlisted-dependency — apps/terminal/package.json:0
   "vp" is used by package.json scripts but not listed in the workspace package.json.

12. [warning] unused-dependency — apps/terminal/package.json:0
   "@xterm/addon-web-fonts" is listed in dependencies but not used.

13. [warning] unused-dev-dependency — apps/terminal/package.json:0
   "jsdom" is listed in devDependencies but not used.

14. [warning] type-only-dependency — apps/terminal/src/lib/terminal-session-info.ts:1
   "zod" is only used in type positions.

15. [warning] runtime-dev-dependency — apps/terminal/src/main.tsx:2
   "react-grab" is imported by runtime code but listed in devDependencies.

16. [warning] runtime-dev-dependency — apps/terminal/vite.config.ts:2
   "@tailwindcss/vite" is imported by runtime code but listed in devDependencies.

17. [warning] runtime-dev-dependency — apps/terminal/vite.config.ts:3
   "@vitejs/plugin-react" is imported by runtime code but listed in devDependencies.

18. [warning] runtime-dev-dependency — apps/terminal/vite.config.ts:4
   "vite" is imported by runtime code but listed in devDependencies.

19. [warning] barrel-hotspot — apps/terminal/src/lib/constants.ts:0
   This module exports 61 symbols and is imported by 43 modules.

20. [warning] barrel-hotspot — apps/terminal/src/lib/terminal-fonts.ts:0
   This module exports 6 symbols and is imported by 6 modules.

21. [warning] barrel-hotspot — apps/terminal/src/utils/favicon-state-store.ts:0
   This module exports 7 symbols and is imported by 4 modules.

22. [warning] rerender-memo-before-early-return — apps/terminal/src/components/ui/field.tsx:173
   useMemo returning JSX runs before an early return - extract the JSX into a memoized child component so the parent bails out before the subtree renders

23. [warning] rerender-memo — apps/terminal/src/components/ui/field.tsx:173
   useMemo returns JSX - extract the expensive subtree into a memoized child component so parent early returns and prop equality can skip the work

24. [warning] no-array-index-as-key — apps/terminal/src/components/ui/field.tsx:190
   Array index "index" used as key - causes bugs when list is reordered or filtered

25. [warning] click-events-have-key-events — apps/terminal/src/components/ui/input-group.tsx:48
   Enforce a clickable non-interactive element has at least one keyboard event listener.

26. [warning] label-has-associated-control — apps/terminal/src/components/ui/label.tsx:9
   A form label must be associated with a control.

27. [warning] js-tosorted-immutable — apps/terminal/src/utils/query-local-fonts.ts:22
   [...array].sort() - use array.toSorted() for immutable sorting (ES2023)

28. [warning] no-autofocus — apps/terminal/src/components/local-font-picker.tsx:72
   The `autoFocus` attribute is found here, which can cause usability issues for sighted and non-sighted users.

29. [warning] no-autofocus — apps/terminal/src/components/local-font-picker.tsx:212
   The `autoFocus` attribute is found here, which can cause usability issues for sighted and non-sighted users.

30. [warning] effect-no-adjust-state-on-prop-change — apps/terminal/src/components/local-font-picker.tsx:111
   state adjusted from a prop-change effect - derive during render or reset state directly while rendering instead of synchronizing after paint

31. [warning] effect-no-adjust-state-on-prop-change — apps/terminal/src/components/local-font-picker.tsx:113
   state adjusted from a prop-change effect - derive during render or reset state directly while rendering instead of synchronizing after paint

32. [warning] async-defer-await — apps/terminal/src/components/local-font-picker.tsx:119
   await blocks the function before an early-return that doesn't use the awaited value - move the await after the synchronous guard so the skip path stays fast

33. [warning] js-tosorted-immutable — apps/terminal/tests/lib/terminal-scrollback.test.ts:11
   [...array].sort() - use array.toSorted() for immutable sorting (ES2023)

34. [warning] exhaustive-deps — apps/terminal/src/components/terminal.tsx:441
   React Hook useEffect has a missing dependency: 'isMac'

35. [warning] prefer-useReducer — apps/terminal/src/components/terminal.tsx:156
   Component "Terminal" has 22 useState calls - consider useReducer for related state

36. [warning] rerender-state-only-in-handlers — apps/terminal/src/components/terminal.tsx:182
   useState "searchOpenAttempt" is updated but never read in the component's return - use useRef so updates don't trigger re-renders

37. [warning] rerender-state-only-in-handlers — apps/terminal/src/components/terminal.tsx:196
   useState "previewFontId" is updated but never read in the component's return - use useRef so updates don't trigger re-renders

38. [warning] rerender-state-only-in-handlers — apps/terminal/src/components/terminal.tsx:207
   useState "previewCursorStyle" is updated but never read in the component's return - use useRef so updates don't trigger re-renders

39. [warning] no-giant-component — apps/terminal/src/components/terminal.tsx:156
   Component "Terminal" is 1003 lines - consider breaking it into smaller focused components

40. [warning] no-prop-callback-in-effect — apps/terminal/src/components/terminal.tsx:930
   useEffect calls prop callback "onModalOpenChange" with local state in deps - this is the "lift state via callback" anti-pattern; lift state into a shared Provider so both sides read the same source

41. [warning] advanced-use-latest — apps/terminal/src/components/terminal.tsx:931
   "onModalOpenChange" in effect deps looks like a callback prop - wrap it with useEffectEvent/useLatest and call the latest ref from the subscription instead of re-subscribing

42. [warning] effect-no-pass-data-to-parent — apps/terminal/src/components/terminal.tsx:930
   effect passes child-owned data "isModalOpen" to parent callback "onModalOpenChange" - move the data ownership to the parent instead

43. [warning] client-event-listeners — apps/terminal/src/app.tsx:12
   global event listener is registered per component instance - share it through a module-level subscription or useSWRSubscription so N components don't add N listeners

44. [warning] client-event-listeners — apps/terminal/src/app.tsx:13
   global event listener is registered per component instance - share it through a module-level subscription or useSWRSubscription so N components don't add N listeners

❌ Errors (1)

effect-needs-cleanup

useEffect subscribes via addEventListener(...) but never returns a cleanup - leaks the registration on every re-run and on unmount. Return a cleanup function that calls the matching remove/unsubscribe call

Return cleanup from effects that register timers, subscriptions, listeners, observers, or async resources.

apps/terminal/src/components/terminal.tsx:221


⚠️ Warnings (43)

unused-type-export

The exported symbol "WebErrorCode" is not referenced by reachable modules.

Remove the export or make it part of an entrypoint API.

apps/terminal/src/lib/errors.ts:52
apps/terminal/src/lib/errors.ts:53
apps/terminal/src/lib/local-fonts-types.ts:1
apps/terminal/src/lib/terminal-themes.ts:3
apps/terminal/src/utils/restore-terminal-scroll-anchor.ts:6

runtime-dev-dependency

"react-grab" is imported by runtime code but listed in devDependencies.

Update the nearest package.json dependency bucket to match actual usage.

apps/terminal/src/main.tsx:2
apps/terminal/vite.config.ts:2
apps/terminal/vite.config.ts:3
apps/terminal/vite.config.ts:4

barrel-hotspot

This module exports 61 symbols and is imported by 43 modules.

Prefer direct imports when the barrel inflates the dependency graph.

apps/terminal/src/lib/constants.ts:0
apps/terminal/src/lib/terminal-fonts.ts:0
apps/terminal/src/utils/favicon-state-store.ts:0

rerender-state-only-in-handlers

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

Avoid subscribing render to state that is only needed inside callbacks; read it on demand or store the transient value in a ref.

apps/terminal/src/components/terminal.tsx:182
apps/terminal/src/components/terminal.tsx:196
apps/terminal/src/components/terminal.tsx:207

unused-export

The exported symbol "clientToServerMessageSchema" is not referenced by reachable modules.

Remove the export or make it part of an entrypoint API.

apps/terminal/src/lib/schemas.ts:2
apps/terminal/src/lib/terminal-fonts.ts:18

unlisted-dependency

"@types/vite/client" is referenced by tsconfig.json but not listed in the workspace package.json.

Update the nearest package.json dependency bucket to match actual usage.

apps/terminal/package.json:0
apps/terminal/package.json:0

js-tosorted-immutable

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

Use toSorted for immutable sorting instead of cloning and mutating arrays with sort.

apps/terminal/src/utils/query-local-fonts.ts:22
apps/terminal/tests/lib/terminal-scrollback.test.ts:11

no-autofocus

The autoFocus attribute is found here, which can cause usability issues for sighted and non-sighted users.

Remove the autoFocus attribute.

apps/terminal/src/components/local-font-picker.tsx:72
apps/terminal/src/components/local-font-picker.tsx:212

effect-no-adjust-state-on-prop-change

state adjusted from a prop-change effect - derive during render or reset state directly while rendering instead of synchronizing after paint

Do not adjust local state from a prop-change effect; either derive the value during render or restructure state so the prop change does not need a synchronizing effect.

apps/terminal/src/components/local-font-picker.tsx:111
apps/terminal/src/components/local-font-picker.tsx:113

client-event-listeners

global event listener is registered per component instance - share it through a module-level subscription or useSWRSubscription so N components don't add N listeners

Share global window/document listeners through one module-level subscription or a shared hook instead of adding one listener per component instance.

apps/terminal/src/app.tsx:12
apps/terminal/src/app.tsx:13

unused-file

This source file is not reachable from any package, framework, test, or support entrypoint.

Remove the file or connect it to a real entrypoint.

apps/terminal/tests/setup.ts:0

unused-dependency

"@xterm/addon-web-fonts" is listed in dependencies but not used.

Update the nearest package.json dependency bucket to match actual usage.

apps/terminal/package.json:0

unused-dev-dependency

"jsdom" is listed in devDependencies but not used.

Update the nearest package.json dependency bucket to match actual usage.

apps/terminal/package.json:0

type-only-dependency

"zod" is only used in type positions.

Update the nearest package.json dependency bucket to match actual usage.

apps/terminal/src/lib/terminal-session-info.ts:1

rerender-memo-before-early-return

useMemo returning JSX runs before an early return - extract the JSX into a memoized child component so the parent bails out before the subtree renders

Move expensive memoized work below early returns by extracting a child component or computing after the bail-out branch.

apps/terminal/src/components/ui/field.tsx:173

rerender-memo

useMemo returns JSX - extract the expensive subtree into a memoized child component so parent early returns and prop equality can skip the work

Extract expensive JSX subtrees into memoized child components so parent renders and early returns can skip their work.

apps/terminal/src/components/ui/field.tsx:173

no-array-index-as-key

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

Use a stable item id for React keys so inserts, deletes, and sorting preserve component state correctly.

apps/terminal/src/components/ui/field.tsx:190

click-events-have-key-events

Enforce a clickable non-interactive element has at least one keyboard event listener.

Visible, non-interactive elements with click handlers must have one of keyup, keydown, or keypress listener.

apps/terminal/src/components/ui/input-group.tsx:48

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.

apps/terminal/src/components/ui/label.tsx:9

async-defer-await

await blocks the function before an early-return that doesn't use the awaited value - move the await after the synchronous guard so the skip path stays fast

Move awaits into the branch that needs the result so other branches can return without waiting.

apps/terminal/src/components/local-font-picker.tsx:119

exhaustive-deps

React Hook useEffect has a missing dependency: 'isMac'

Either include it or remove the dependency array.

apps/terminal/src/components/terminal.tsx:441

prefer-useReducer

Component "Terminal" has 22 useState calls - consider useReducer for related state

Replace clusters of related useState setters with a reducer so transitions are explicit and multi-field updates happen together.

apps/terminal/src/components/terminal.tsx:156

no-giant-component

Component "Terminal" is 1003 lines - consider breaking it into smaller focused components

Split large components by responsibility into smaller components or hooks so rendering, state, and effects stay local.

apps/terminal/src/components/terminal.tsx:156

no-prop-callback-in-effect

useEffect calls prop callback "onModalOpenChange" with local state in deps - this is the "lift state via callback" anti-pattern; lift state into a shared Provider so both sides read the same source

Wrap callback props with useEffectEvent/useLatest or call them from the originating event so effects do not re-run only because callback identity changed.

apps/terminal/src/components/terminal.tsx:930

advanced-use-latest

"onModalOpenChange" in effect deps looks like a callback prop - wrap it with useEffectEvent/useLatest and call the latest ref from the subscription instead of re-subscribing

Wrap callback props with useEffectEvent or a useLatest ref and call the latest value from subscriptions instead of re-subscribing on every render.

apps/terminal/src/components/terminal.tsx:931

effect-no-pass-data-to-parent

effect passes child-owned data "isModalOpen" to parent callback "onModalOpenChange" - move the data ownership to the parent instead

Do not fetch or derive child-owned data and push it to a parent from an effect; fetch in the parent and pass data down, or return data from the hook.

apps/terminal/src/components/terminal.tsx:930


Last scored May 13, 2026 at 2:13 PM UTC. Maintained by React Review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions