#1

React Review Audit

Openreact-doctor[bot]opened 6mo ago

Score: 69/100 · 8 errors · 133 warnings

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

## Errors (8)

1. [error] alt-text — src/modules/shared/components/Sidebar/Sidebar.tsx:17
   Missing `alt` attribute.

2. [error] alt-text — src/modules/shared/components/Sidebar/Sidebar.tsx:17
   Missing `alt` attribute.

3. [error] jsx-key — src/modules/editor/plugins/MentionsPlugin/index.tsx:621
   Missing "key" prop for element in iterator.

4. [error] effect-needs-cleanup — src/modules/editor/plugins/TableCellResizer/index.tsx:87
   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(...)

5. [error] effect-needs-cleanup — src/modules/editor/plugins/SpeechToTextPlugin/index.ts:57
   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

6. [error] alt-text — src/modules/editor/nodes/ImageComponent.tsx:126
   Missing `alt` attribute.

7. [error] no-nested-component-definition — src/modules/editor/nodes/ExcalidrawNode/ExcalidrawModal.tsx:183
   Component "ShowDiscardDialog" defined inside "ExcalidrawModal" — creates new instance every render, destroying state

8. [error] effect-needs-cleanup — src/modules/editor/nodes/StickyComponent.tsx:133
   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 (133)

9. [warning] click-events-have-key-events — src/modules/editor/plugins/EmojiPickerPlugin/index.tsx:60
   Enforce a clickable non-interactive element has at least one keyboard event listener.

10. [warning] no-array-index-as-key — src/modules/shared/components/SidebarItems/SidebarItems.tsx:19
   Array index "index" used as key — causes bugs when list is reordered or filtered

11. [warning] click-events-have-key-events — src/modules/editor/plugins/ContextMenuPlugin/index.tsx:43
   Enforce a clickable non-interactive element has at least one keyboard event listener.

12. [warning] async-await-in-loop — src/modules/editor/plugins/ContextMenuPlugin/index.tsx:136
   await inside a for…of loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently

13. [warning] click-events-have-key-events — src/modules/editor/plugins/ComponentPickerPlugin/index.tsx:101
   Enforce a clickable non-interactive element has at least one keyboard event listener.

14. [warning] no-array-index-as-key — src/modules/editor/plugins/ComponentPickerPlugin/index.tsx:342
   Array index "index" used as key — causes bugs when list is reordered or filtered

15. [warning] async-await-in-loop — src/modules/editor/utils/docSerialization.ts:17
   await inside a while-loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently

16. [warning] click-events-have-key-events — src/modules/shared/components/Navbar/Navbar.tsx:108
   Enforce a clickable non-interactive element has at least one keyboard event listener.

17. [warning] click-events-have-key-events — src/modules/shared/components/Navbar/Navbar.tsx:116
   Enforce a clickable non-interactive element has at least one keyboard event listener.

18. [warning] click-events-have-key-events — src/modules/shared/components/Navbar/Navbar.tsx:125
   Enforce a clickable non-interactive element has at least one keyboard event listener.

19. [warning] no-derived-useState — src/modules/editor/ui/KatexEquationAlterer.tsx:28
   useState initialized from prop "initialEquation" — if this value should stay in sync with the prop, derive it during render instead

20. [warning] no-default-props — src/modules/shared/components/LazyLoad/LazyLoad.tsx:24
   LazyLoad.defaultProps — React 19 removes `defaultProps` for function components and discourages it for class components. Move defaults into the destructured props parameter (e.g. `function LazyLoad({ size = "md", ...rest })`) so the rule applies cleanly to both shapes

21. [warning] js-batch-dom-css — src/modules/editor/ui/ImageResizer.tsx:169
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

22. [warning] js-batch-dom-css — src/modules/editor/ui/ImageResizer.tsx:199
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

23. [warning] no-array-index-as-key — src/modules/shared/routes/index.tsx:27
   Array index "index" used as key — causes bugs when list is reordered or filtered

24. [warning] click-events-have-key-events — src/modules/shared/components/Input/Input.tsx:69
   Enforce a clickable non-interactive element has at least one keyboard event listener.

25. [warning] no-default-props — src/modules/shared/components/Input/Input.tsx:90
   Input.defaultProps — React 19 removes `defaultProps` for function components and discourages it for class components. Move defaults into the destructured props parameter (e.g. `function Input({ size = "md", ...rest })`) so the rule applies cleanly to both shapes

26. [warning] no-autofocus — src/modules/editor/ui/EquationEditor.tsx:36
   The `autoFocus` attribute is found here, which can cause usability issues for sighted and non-sighted users.

27. [warning] no-react19-deprecated-apis — src/modules/editor/ui/EquationEditor.tsx:13
   forwardRef is no longer needed on React 19+ — refs are regular props on function components; remove forwardRef and pass ref directly

28. [warning] click-events-have-key-events — src/modules/shared/components/DropDown/DropDown.tsx:48
   Enforce a clickable non-interactive element has at least one keyboard event listener.

29. [warning] no-static-element-interactions — src/modules/shared/components/DropDown/DropDown.tsx:48
   Static HTML elements with event handlers require a role.

30. [warning] click-events-have-key-events — src/modules/dashboard/features/Test.tsx:18
   Enforce a clickable non-interactive element has at least one keyboard event listener.

31. [warning] no-static-element-interactions — src/modules/dashboard/features/Test.tsx:18
   Static HTML elements with event handlers require a role.

32. [warning] no-static-element-interactions — src/modules/editor/ui/DropDown.tsx:127
   Static HTML elements with event handlers require a role.

33. [warning] no-react19-deprecated-apis — src/modules/editor/ui/DropDown.tsx:37
   useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

34. [warning] rerender-state-only-in-handlers — src/modules/editor/ui/DropDown.tsx:68
   useState "highlightedItem" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

35. [warning] js-batch-dom-css — src/modules/editor/ui/DropDown.tsx:171
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

36. [warning] client-passive-event-listeners — src/modules/editor/ui/DropDown.tsx:213
   "scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

37. [warning] no-default-props — src/modules/shared/components/Button/Button.tsx:67
   Button.defaultProps — React 19 removes `defaultProps` for function components and discourages it for class components. Move defaults into the destructured props parameter (e.g. `function Button({ size = "md", ...rest })`) so the rule applies cleanly to both shapes

38. [warning] no-static-element-interactions — src/modules/editor/ui/ColorPicker.tsx:211
   Static HTML elements with event handlers require a role.

39. [warning] no-effect-chain — src/modules/editor/ui/ColorPicker.tsx:92
   useEffect reacts to "selfColor" which is set by another useEffect — chains of effects add an extra render per link and become rigid as code evolves. Compute what you can during render and write all related state inside the event handler that originally fires the chain

40. [warning] rerender-lazy-state-init — src/modules/editor/ui/ColorPicker.tsx:46
   useState(transformColor()) calls initializer on every render — use useState(() => transformColor()) for lazy initialization

41. [warning] no-derived-useState — src/modules/editor/ui/ColorPicker.tsx:47
   useState initialized from prop "color" — if this value should stay in sync with the prop, derive it during render instead

42. [warning] no-prop-callback-in-effect — src/modules/editor/ui/ColorPicker.tsx:95
   useEffect calls prop callback "onChange" 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

43. [warning] click-events-have-key-events — src/modules/editor/plugins/CommentPlugin/index.tsx:570
   Enforce a clickable non-interactive element has at least one keyboard event listener.

44. [warning] js-batch-dom-css — src/modules/editor/plugins/CommentPlugin/index.tsx:86
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

45. [warning] advanced-event-handler-refs — src/modules/editor/plugins/CommentPlugin/index.tsx:90
   useEffect re-subscribes a "updatePosition" listener every time the handler identity changes — store the handler in a ref and have the listener read `handlerRef.current()`, then drop it from the deps

46. [warning] js-batch-dom-css — src/modules/editor/plugins/CommentPlugin/index.tsx:237
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

47. [warning] advanced-event-handler-refs — src/modules/editor/plugins/CommentPlugin/index.tsx:283
   useEffect re-subscribes a "updateLocation" listener every time the handler identity changes — store the handler in a ref and have the listener read `handlerRef.current()`, then drop it from the deps

48. [warning] rerender-state-only-in-handlers — src/modules/editor/plugins/CommentPlugin/index.tsx:511
   useState "counter" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

49. [warning] js-hoist-intl — src/modules/editor/plugins/CommentPlugin/index.tsx:515
   new Intl.RelativeTimeFormat() inside a function — hoist to module scope or wrap in useMemo so it isn't recreated each call

50. [warning] rerender-functional-setstate — src/modules/editor/plugins/CommentPlugin/index.tsx:526
   setCounter(counter + ...) — use functional update to avoid stale closures

51. [warning] no-effect-event-handler — src/modules/editor/plugins/CommentPlugin/index.tsx:696
   useEffect simulating an event handler — move logic to an actual event handler instead

52. [warning] no-prop-callback-in-effect — src/modules/editor/plugins/CommentPlugin/index.tsx:698
   useEffect calls prop callback "providerFactory" 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

53. [warning] no-cascading-set-state — src/modules/editor/plugins/CommentPlugin/index.tsx:797
   6 setState calls in a single useEffect — consider using useReducer or deriving state

54. [warning] client-passive-event-listeners — src/modules/editor/utils/swipe.ts:52
   "touchstart" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

55. [warning] client-passive-event-listeners — src/modules/editor/utils/swipe.ts:53
   "touchend" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

56. [warning] js-batch-dom-css — src/modules/editor/utils/setFloatingElemPositionForLinkEditor.ts:45
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

57. [warning] js-batch-dom-css — src/modules/editor/utils/setFloatingElemPositionForLinkEditor.ts:22
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

58. [warning] js-batch-dom-css — src/modules/editor/utils/setFloatingElemPosition.ts:50
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

59. [warning] js-batch-dom-css — src/modules/editor/utils/setFloatingElemPosition.ts:23
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

60. [warning] click-events-have-key-events — src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:233
   Enforce a clickable non-interactive element has at least one keyboard event listener.

61. [warning] click-events-have-key-events — src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:243
   Enforce a clickable non-interactive element has at least one keyboard event listener.

62. [warning] click-events-have-key-events — src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:260
   Enforce a clickable non-interactive element has at least one keyboard event listener.

63. [warning] click-events-have-key-events — src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:270
   Enforce a clickable non-interactive element has at least one keyboard event listener.

64. [warning] client-passive-event-listeners — src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:127
   "scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

65. [warning] no-cascading-set-state — src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:294
   4 setState calls in a single useEffect — consider using useReducer or deriving state

66. [warning] no-full-lodash-import — src/modules/editor/plugins/CodeActionMenuPlugin/utils.ts:8
   Importing entire lodash library — import from 'lodash/functionName' instead

67. [warning] rerender-state-only-in-handlers — src/modules/editor/plugins/CodeActionMenuPlugin/index.tsx:43
   useState "shouldListenMouseMove" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

68. [warning] advanced-event-handler-refs — src/modules/editor/plugins/CodeActionMenuPlugin/index.tsx:98
   useEffect re-subscribes a "debouncedOnMouseMove" listener every time the handler identity changes — store the handler in a ref and have the listener read `handlerRef.current()`, then drop it from the deps

69. [warning] js-set-map-lookups — src/modules/editor/commenting/index.ts:190
   array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

70. [warning] js-index-maps — src/modules/editor/commenting/index.ts:351
   array.find() in a loop is O(n*m) — build a Map for O(1) lookups

71. [warning] rerender-lazy-state-init — src/modules/editor/commenting/index.ts:446
   useState(getComments()) calls initializer on every render — use useState(() => getComments()) for lazy initialization

72. [warning] no-generic-handler-names — src/modules/editor/plugins/CodeActionMenuPlugin/components/PrettierButton/index.tsx:138
   Non-descriptive handler name "handleClick" — name should describe what it does, not when it runs

73. [warning] no-generic-handler-names — src/modules/editor/plugins/CodeActionMenuPlugin/components/CopyButton/index.tsx:61
   Non-descriptive handler name "handleClick" — name should describe what it does, not when it runs

74. [warning] click-events-have-key-events — src/modules/editor/plugins/AutoEmbedPlugin/index.tsx:175
   Enforce a clickable non-interactive element has at least one keyboard event listener.

75. [warning] no-effect-event-handler — src/modules/editor/plugins/PasteLogPlugin/index.tsx:19
   useEffect simulating an event handler — move logic to an actual event handler instead

76. [warning] no-many-boolean-props — src/modules/editor/plugins/FloatingTextFormatToolbarPlugin/index.tsx:41
   Component "TextFormatFloatingToolbar" takes 5 boolean-like props (isLink, isBold, isItalic…) — consider compound components or explicit variants instead of stacking flags

77. [warning] client-passive-event-listeners — src/modules/editor/plugins/FloatingTextFormatToolbarPlugin/index.tsx:148
   "scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

78. [warning] advanced-event-handler-refs — src/modules/editor/plugins/FloatingTextFormatToolbarPlugin/index.tsx:354
   useEffect re-subscribes a "updatePopup" listener every time the handler identity changes — store the handler in a ref and have the listener read `handlerRef.current()`, then drop it from the deps

79. [warning] prefer-useReducer — src/modules/editor/plugins/ToolbarPlugin/index.tsx:538
   Component "ToolbarPlugin" has 23 useState calls — consider useReducer for related state

80. [warning] no-giant-component — src/modules/editor/plugins/ToolbarPlugin/index.tsx:534
   Component "ToolbarPlugin" is 758 lines — consider breaking it into smaller focused components

81. [warning] no-cascading-set-state — src/modules/editor/plugins/ToolbarPlugin/index.tsx:710
   3 setState calls in a single useEffect — consider using useReducer or deriving state

82. [warning] click-events-have-key-events — src/modules/editor/plugins/TableOfContentsPlugin/index.tsx:132
   Enforce a clickable non-interactive element has at least one keyboard event listener.

83. [warning] click-events-have-key-events — src/modules/editor/plugins/TableOfContentsPlugin/index.tsx:151
   Enforce a clickable non-interactive element has at least one keyboard event listener.

84. [warning] no-cascading-set-state — src/modules/editor/plugins/TableOfContentsPlugin/index.tsx:59
   3 setState calls in a single useEffect — consider using useReducer or deriving state

85. [warning] client-passive-event-listeners — src/modules/editor/plugins/TableOfContentsPlugin/index.tsx:121
   "scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

86. [warning] click-events-have-key-events — src/modules/editor/plugins/MentionsPlugin/index.tsx:591
   Enforce a clickable non-interactive element has at least one keyboard event listener.

87. [warning] no-cascading-set-state — src/modules/editor/plugins/MentionsPlugin/index.tsx:507
   3 setState calls in a single useEffect — consider using useReducer or deriving state

88. [warning] no-mirror-prop-effect — src/modules/editor/plugins/ToolbarPlugin/fontSize.tsx:185
   useState "inputValue" is mirrored from prop "selectionFontSize" via this effect — delete both the useState and the effect, and read the prop directly in render

89. [warning] no-derived-useState — src/modules/editor/plugins/ToolbarPlugin/fontSize.tsx:35
   useState initialized from prop "selectionFontSize" — if this value should stay in sync with the prop, derive it during render instead

90. [warning] no-derived-state-effect — src/modules/editor/plugins/ToolbarPlugin/fontSize.tsx:185
   Derived state in useEffect — compute during render instead

91. [warning] rerender-state-only-in-handlers — src/modules/editor/plugins/TableHoverActionsPlugin/index.tsx:39
   useState "shouldListenMouseMove" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

92. [warning] advanced-event-handler-refs — src/modules/editor/plugins/TableHoverActionsPlugin/index.tsx:134
   useEffect re-subscribes a "debouncedOnMouseMove" listener every time the handler identity changes — store the handler in a ref and have the listener read `handlerRef.current()`, then drop it from the deps

93. [warning] js-batch-dom-css — src/modules/editor/plugins/TestRecorderPlugin/index.tsx:22
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

94. [warning] js-set-map-lookups — src/modules/editor/plugins/TestRecorderPlugin/index.tsx:123
   array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

95. [warning] no-effect-event-handler — src/modules/editor/plugins/TestRecorderPlugin/index.tsx:298
   useEffect simulating an event handler — move logic to an actual event handler instead

96. [warning] no-static-element-interactions — src/modules/editor/plugins/TableCellResizer/index.tsx:409
   Static HTML elements with event handlers require a role.

97. [warning] no-static-element-interactions — src/modules/editor/plugins/TableCellResizer/index.tsx:414
   Static HTML elements with event handlers require a role.

98. [warning] no-giant-component — src/modules/editor/plugins/TableCellResizer/index.tsx:47
   Component "TableCellResizer" is 377 lines — consider breaking it into smaller focused components

99. [warning] no-cascading-set-state — src/modules/editor/plugins/TableCellResizer/index.tsx:87
   3 setState calls in a single useEffect — consider using useReducer or deriving state

100. [warning] no-react19-deprecated-apis — src/modules/editor/plugins/TablePlugin/TablePlugin.tsx:25
   useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

101. [warning] click-events-have-key-events — src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:473
   Enforce a clickable non-interactive element has at least one keyboard event listener.

102. [warning] prefer-useReducer — src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:124
   Component "TableActionMenu" has 5 useState calls — consider useReducer for related state

103. [warning] rerender-state-only-in-handlers — src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:132
   useState "canMergeCells" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

104. [warning] rerender-state-only-in-handlers — src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:133
   useState "canUnmergeCell" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

105. [warning] no-giant-component — src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:117
   Component "TableActionMenu" is 480 lines — consider breaking it into smaller focused components

106. [warning] no-derived-useState — src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:127
   useState initialized from prop "_tableCellNode" — if this value should stay in sync with the prop, derive it during render instead

107. [warning] js-batch-dom-css — src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:679
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

108. [warning] js-batch-dom-css — src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:682
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

109. [warning] no-react19-deprecated-apis — src/modules/editor/plugins/TablePlugin.tsx:25
   useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

110. [warning] rendering-conditional-render — src/modules/editor/components/editor/editor.tsx:374
   Conditional rendering with a numeric value can render '0' — use `value > 0`, `Boolean(value)`, or a ternary

111. [warning] no-derived-useState — src/modules/editor/nodes/EquationComponent.tsx:39
   useState initialized from prop "equation" — if this value should stay in sync with the prop, derive it during render instead

112. [warning] no-effect-event-handler — src/modules/editor/nodes/EquationComponent.tsx:65
   useEffect simulating an event handler — move logic to an actual event handler instead

113. [warning] rerender-state-only-in-handlers — src/modules/editor/plugins/SpeechToTextPlugin/index.ts:50
   useState "isEnabled" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

114. [warning] rerender-lazy-state-init — src/modules/editor/nodes/InlineImageNode/InlineImageComponent.tsx:109
   useState(getAltText()) calls initializer on every render — use useState(() => getAltText()) for lazy initialization

115. [warning] rerender-lazy-state-init — src/modules/editor/nodes/InlineImageNode/InlineImageComponent.tsx:110
   useState(getShowCaption()) calls initializer on every render — use useState(() => getShowCaption()) for lazy initialization

116. [warning] rerender-lazy-state-init — src/modules/editor/nodes/InlineImageNode/InlineImageComponent.tsx:111
   useState(getPosition()) calls initializer on every render — use useState(() => getPosition()) for lazy initialization

117. [warning] no-cascading-set-state — src/modules/editor/nodes/InlineImageNode/InlineImageComponent.tsx:275
   3 setState calls in a single useEffect — consider using useReducer or deriving state

118. [warning] no-react19-deprecated-apis — src/modules/editor/context/SharedHistoryContext.tsx:13
   useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

119. [warning] js-batch-dom-css — src/modules/editor/hooks/useReport.ts:18
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

120. [warning] js-batch-dom-css — src/modules/editor/hooks/useReport.ts:19
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

121. [warning] js-batch-dom-css — src/modules/editor/hooks/useReport.ts:20
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

122. [warning] js-batch-dom-css — src/modules/editor/hooks/useReport.ts:21
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

123. [warning] js-batch-dom-css — src/modules/editor/hooks/useReport.ts:22
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

124. [warning] js-batch-dom-css — src/modules/editor/hooks/useReport.ts:23
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

125. [warning] js-batch-dom-css — src/modules/editor/hooks/useReport.ts:24
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

126. [warning] no-giant-component — src/modules/editor/nodes/ImageComponent.tsx:138
   Component "ImageComponent" is 311 lines — consider breaking it into smaller focused components

127. [warning] advanced-event-handler-refs — src/modules/editor/nodes/ImageComponent.tsx:273
   useEffect re-subscribes a "onRightClick" listener every time the handler identity changes — store the handler in a ref and have the listener read `handlerRef.current()`, then drop it from the deps

128. [warning] no-react19-deprecated-apis — src/modules/editor/context/SharedAutocompleteContext.tsx:13
   useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

129. [warning] iframe-has-title — src/modules/editor/nodes/FigmaNode.tsx:45
   Missing `title` attribute for the `iframe` element.

130. [warning] no-react19-deprecated-apis — src/modules/editor/context/SettingsContext.tsx:12
   useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

131. [warning] js-batch-dom-css — src/modules/editor/nodes/ExcalidrawNode/index.tsx:142
   Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

132. [warning] no-react19-deprecated-apis — src/modules/editor/context/FlashMessageContext.tsx:9
   useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

133. [warning] no-effect-event-handler — src/modules/editor/context/FlashMessageContext.tsx:30
   useEffect simulating an event handler — move logic to an actual event handler instead

134. [warning] no-danger — src/modules/editor/nodes/ExcalidrawNode/ExcalidrawImage.tsx:129
   Do not use `dangerouslySetInnerHTML` prop

135. [warning] no-derived-useState — src/modules/editor/nodes/ExcalidrawNode/ExcalidrawModal.tsx:92
   useState initialized from prop "initialElements" — if this value should stay in sync with the prop, derive it during render instead

136. [warning] no-derived-useState — src/modules/editor/nodes/ExcalidrawNode/ExcalidrawModal.tsx:93
   useState initialized from prop "initialFiles" — if this value should stay in sync with the prop, derive it during render instead

137. [warning] click-events-have-key-events — src/modules/editor/nodes/ExcalidrawNode/ExcalidrawComponent.tsx:227
   Enforce a clickable non-interactive element has at least one keyboard event listener.

138. [warning] no-effect-event-handler — src/modules/editor/nodes/ExcalidrawNode/ExcalidrawComponent.tsx:65
   useEffect simulating an event handler — move logic to an actual event handler instead

139. [warning] no-render-in-render — src/app/App.tsx:23
   Inline render function "renderRoutes()" — extract to a separate component for proper reconciliation

140. [warning] no-cascading-set-state — src/modules/editor/plugins/ActionsPlugin/index.tsx:137
   3 setState calls in a single useEffect — consider using useReducer or deriving state

141. [warning] rendering-hydration-mismatch-time — src/modules/editor/plugins/ActionsPlugin/index.tsx:228
   new Date() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

❌ Errors (8)

alt-text

Missing alt attribute.

Must have alt prop, either with meaningful text, or an empty string for decorative images.

src/modules/shared/components/Sidebar/Sidebar.tsx:17
src/modules/shared/components/Sidebar/Sidebar.tsx:17
src/modules/editor/nodes/ImageComponent.tsx:126

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 () => target.removeEventListener(name, handler) for listeners, return () => clearInterval(id) / clearTimeout(id) for timers, or return unsubscribe if the subscribe call already returned one

src/modules/editor/plugins/TableCellResizer/index.tsx:87
src/modules/editor/plugins/SpeechToTextPlugin/index.ts:57
src/modules/editor/nodes/StickyComponent.tsx:133

jsx-key

Missing "key" prop for element in iterator.

Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key).

src/modules/editor/plugins/MentionsPlugin/index.tsx:621

no-nested-component-definition

Component "ShowDiscardDialog" defined inside "ExcalidrawModal" — creates new instance every render, destroying state

Move to a separate file or to module scope above the parent component

src/modules/editor/nodes/ExcalidrawNode/ExcalidrawModal.tsx:183


⚠️ Warnings (133)

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.

src/modules/editor/plugins/EmojiPickerPlugin/index.tsx:60
src/modules/editor/plugins/ContextMenuPlugin/index.tsx:43
src/modules/editor/plugins/ComponentPickerPlugin/index.tsx:101
src/modules/shared/components/Navbar/Navbar.tsx:108
src/modules/shared/components/Navbar/Navbar.tsx:116
src/modules/shared/components/Navbar/Navbar.tsx:125
src/modules/shared/components/Input/Input.tsx:69
src/modules/shared/components/DropDown/DropDown.tsx:48
src/modules/dashboard/features/Test.tsx:18
src/modules/editor/plugins/CommentPlugin/index.tsx:570
src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:233
src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:243
src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:260
src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:270
src/modules/editor/plugins/AutoEmbedPlugin/index.tsx:175
src/modules/editor/plugins/TableOfContentsPlugin/index.tsx:132
src/modules/editor/plugins/TableOfContentsPlugin/index.tsx:151
src/modules/editor/plugins/MentionsPlugin/index.tsx:591
src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:473
src/modules/editor/nodes/ExcalidrawNode/ExcalidrawComponent.tsx:227

js-batch-dom-css

Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

Batch DOM/CSS reads and writes — interleaving them inside a loop causes layout thrashing. Read first, then write

src/modules/editor/ui/ImageResizer.tsx:169
src/modules/editor/ui/ImageResizer.tsx:199
src/modules/editor/ui/DropDown.tsx:171
src/modules/editor/plugins/CommentPlugin/index.tsx:86
src/modules/editor/plugins/CommentPlugin/index.tsx:237
src/modules/editor/utils/setFloatingElemPositionForLinkEditor.ts:45
src/modules/editor/utils/setFloatingElemPositionForLinkEditor.ts:22
src/modules/editor/utils/setFloatingElemPosition.ts:50
src/modules/editor/utils/setFloatingElemPosition.ts:23
src/modules/editor/plugins/TestRecorderPlugin/index.tsx:22
src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:679
src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:682
src/modules/editor/hooks/useReport.ts:18
src/modules/editor/hooks/useReport.ts:19
src/modules/editor/hooks/useReport.ts:20
src/modules/editor/hooks/useReport.ts:21
src/modules/editor/hooks/useReport.ts:22
src/modules/editor/hooks/useReport.ts:23
src/modules/editor/hooks/useReport.ts:24
src/modules/editor/nodes/ExcalidrawNode/index.tsx:142

no-react19-deprecated-apis

forwardRef is no longer needed on React 19+ — refs are regular props on function components; remove forwardRef and pass ref directly

Pass ref as a regular prop on function components — forwardRef is no longer needed in React 19+. Replace useContext(X) with use(X) for branch-aware context reads. Only enabled on projects detected as React 19+.

src/modules/editor/ui/EquationEditor.tsx:13
src/modules/editor/ui/DropDown.tsx:37
src/modules/editor/plugins/TablePlugin/TablePlugin.tsx:25
src/modules/editor/plugins/TablePlugin.tsx:25
src/modules/editor/context/SharedHistoryContext.tsx:13
src/modules/editor/context/SharedAutocompleteContext.tsx:13
src/modules/editor/context/SettingsContext.tsx:12
src/modules/editor/context/FlashMessageContext.tsx:9

no-cascading-set-state

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

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

src/modules/editor/plugins/CommentPlugin/index.tsx:797
src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:294
src/modules/editor/plugins/ToolbarPlugin/index.tsx:710
src/modules/editor/plugins/TableOfContentsPlugin/index.tsx:59
src/modules/editor/plugins/MentionsPlugin/index.tsx:507
src/modules/editor/plugins/TableCellResizer/index.tsx:87
src/modules/editor/nodes/InlineImageNode/InlineImageComponent.tsx:275
src/modules/editor/plugins/ActionsPlugin/index.tsx:137

no-derived-useState

useState initialized from prop "initialEquation" — if this value should stay in sync with the prop, derive it during render instead

Remove useState and compute the value inline: const value = transform(propName)

src/modules/editor/ui/KatexEquationAlterer.tsx:28
src/modules/editor/ui/ColorPicker.tsx:47
src/modules/editor/plugins/ToolbarPlugin/fontSize.tsx:35
src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:127
src/modules/editor/nodes/EquationComponent.tsx:39
src/modules/editor/nodes/ExcalidrawNode/ExcalidrawModal.tsx:92
src/modules/editor/nodes/ExcalidrawNode/ExcalidrawModal.tsx:93

rerender-state-only-in-handlers

useState "highlightedItem" 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

src/modules/editor/ui/DropDown.tsx:68
src/modules/editor/plugins/CommentPlugin/index.tsx:511
src/modules/editor/plugins/CodeActionMenuPlugin/index.tsx:43
src/modules/editor/plugins/TableHoverActionsPlugin/index.tsx:39
src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:132
src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:133
src/modules/editor/plugins/SpeechToTextPlugin/index.ts:50

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.

src/modules/shared/components/DropDown/DropDown.tsx:48
src/modules/dashboard/features/Test.tsx:18
src/modules/editor/ui/DropDown.tsx:127
src/modules/editor/ui/ColorPicker.tsx:211
src/modules/editor/plugins/TableCellResizer/index.tsx:409
src/modules/editor/plugins/TableCellResizer/index.tsx:414

client-passive-event-listeners

"scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

Add { passive: true } as the third argument: addEventListener('scroll', handler, { passive: true }). Only do this if the handler does NOT call event.preventDefault() — passive listeners silently ignore preventDefault(), which breaks features like pull-to-refresh suppression, custom gestures, and nested-scroll containment.

src/modules/editor/ui/DropDown.tsx:213
src/modules/editor/utils/swipe.ts:52
src/modules/editor/utils/swipe.ts:53
src/modules/editor/plugins/FloatingLinkEditorPlugin/index.tsx:127
src/modules/editor/plugins/FloatingTextFormatToolbarPlugin/index.tsx:148
src/modules/editor/plugins/TableOfContentsPlugin/index.tsx:121

advanced-event-handler-refs

useEffect re-subscribes a "updatePosition" listener every time the handler identity changes — store the handler in a ref and have the listener read handlerRef.current(), then drop it from the deps

Store the handler in a ref and have the listener read handlerRef.current() — the subscription stays put while the latest handler is always called

src/modules/editor/plugins/CommentPlugin/index.tsx:90
src/modules/editor/plugins/CommentPlugin/index.tsx:283
src/modules/editor/plugins/CodeActionMenuPlugin/index.tsx:98
src/modules/editor/plugins/FloatingTextFormatToolbarPlugin/index.tsx:354
src/modules/editor/plugins/TableHoverActionsPlugin/index.tsx:134
src/modules/editor/nodes/ImageComponent.tsx:273

no-effect-event-handler

useEffect simulating an event handler — move logic to an actual event handler instead

Move the conditional logic into onClick, onChange, or onSubmit handlers directly

src/modules/editor/plugins/CommentPlugin/index.tsx:696
src/modules/editor/plugins/PasteLogPlugin/index.tsx:19
src/modules/editor/plugins/TestRecorderPlugin/index.tsx:298
src/modules/editor/nodes/EquationComponent.tsx:65
src/modules/editor/context/FlashMessageContext.tsx:30
src/modules/editor/nodes/ExcalidrawNode/ExcalidrawComponent.tsx:65

rerender-lazy-state-init

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

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

src/modules/editor/ui/ColorPicker.tsx:46
src/modules/editor/commenting/index.ts:446
src/modules/editor/nodes/InlineImageNode/InlineImageComponent.tsx:109
src/modules/editor/nodes/InlineImageNode/InlineImageComponent.tsx:110
src/modules/editor/nodes/InlineImageNode/InlineImageComponent.tsx:111

no-giant-component

Component "ToolbarPlugin" is 758 lines — consider breaking it into smaller focused components

Extract logical sections into focused components: <UserHeader />, <UserActions />, etc.

src/modules/editor/plugins/ToolbarPlugin/index.tsx:534
src/modules/editor/plugins/TableCellResizer/index.tsx:47
src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:117
src/modules/editor/nodes/ImageComponent.tsx:138

no-array-index-as-key

Array index "index" 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

src/modules/shared/components/SidebarItems/SidebarItems.tsx:19
src/modules/editor/plugins/ComponentPickerPlugin/index.tsx:342
src/modules/shared/routes/index.tsx:27

no-default-props

LazyLoad.defaultProps — React 19 removes defaultProps for function components and discourages it for class components. Move defaults into the destructured props parameter (e.g. function LazyLoad({ size = "md", ...rest })) so the rule applies cleanly to both shapes

React 19 removes Component.defaultProps for function components. Move the defaults into the destructured props parameter: function Foo({ size = "md", variant = "primary" }) instead of Foo.defaultProps = { size: "md", variant: "primary" }.

src/modules/shared/components/LazyLoad/LazyLoad.tsx:24
src/modules/shared/components/Input/Input.tsx:90
src/modules/shared/components/Button/Button.tsx:67

async-await-in-loop

await inside a for…of loop runs the calls sequentially — for independent operations, collect them and use await Promise.all(items.map(...)) to run them concurrently

Collect the items and use await Promise.all(items.map(...)) to run independent operations concurrently

src/modules/editor/plugins/ContextMenuPlugin/index.tsx:136
src/modules/editor/utils/docSerialization.ts:17

no-prop-callback-in-effect

useEffect calls prop callback "onChange" 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

Lift the shared state into a Provider so both sides read the same source — no useEffect-driven sync needed

src/modules/editor/ui/ColorPicker.tsx:95
src/modules/editor/plugins/CommentPlugin/index.tsx:698

js-set-map-lookups

array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

Use a Set or Map for repeated membership tests / keyed lookups — Array.includes/find is O(n) per call

src/modules/editor/commenting/index.ts:190
src/modules/editor/plugins/TestRecorderPlugin/index.tsx:123

no-generic-handler-names

Non-descriptive handler name "handleClick" — name should describe what it does, not when it runs

Rename to describe the action: e.g. handleSubmit → saveUserProfile, handleClick → toggleSidebar

src/modules/editor/plugins/CodeActionMenuPlugin/components/PrettierButton/index.tsx:138
src/modules/editor/plugins/CodeActionMenuPlugin/components/CopyButton/index.tsx:61

prefer-useReducer

Component "ToolbarPlugin" has 23 useState calls — consider useReducer for related state

Group related state: const [state, dispatch] = useReducer(reducer, { field1, field2, ... })

src/modules/editor/plugins/ToolbarPlugin/index.tsx:538
src/modules/editor/plugins/TableActionMenuPlugin/index.tsx:124

no-autofocus

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

Remove the autoFocus attribute.

src/modules/editor/ui/EquationEditor.tsx:36

no-effect-chain

useEffect reacts to "selfColor" which is set by another useEffect — chains of effects add an extra render per link and become rigid as code evolves. Compute what you can during render and write all related state inside the event handler that originally fires the chain

Compute as much as possible during render (e.g. const isGameOver = round > 5) and write all related state inside the event handler that originally fires the chain. Each ef


Issue body truncated. See the full report at react.review/dashboard.

Activity

0

No activity yet.