aidenybai / aidenybai/react-scan

React Review Audit

Đang mở
#446 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
TypeScript
Star
21.8k
Fork
390
Merge trung bình
23 phút
Pull request đã merge (30 ngày)
1

Mô tả

10 errors, ⚠️ 228 warnings
81 score

Copy as prompt

```text
Fix the following React Review diagnostics in my codebase.

## Errors (10)

1. [error] jsx-key — packages/scan/src/web/views/notifications/collapsed-event.tsx:165
Missing "key" prop for element in iterator.

2. [error] no-mutable-in-deps — packages/scan/src/web/views/notifications/popover.tsx:73
"triggerRef.current" in deps — refs are mutable and don't trigger re-renders, so React won't re-run this effect when it changes. Read the ref inside the effect body instead

3. [error] effect-needs-cleanup — packages/scan/src/web/views/notifications/notifications.tsx:227
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(...)

4. [error] effect-needs-cleanup — packages/scan/src/web/views/notifications/slowdown-history.tsx:27
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 — packages/scan/src/web/views/notifications/slowdown-history.tsx:339
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(...)

6. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "B" — client components cannot be async

7. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "Pa" — client components cannot be async

8. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "Ul" — client components cannot be async

9. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "Jl" — client components cannot be async

10. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "Tc" — client components cannot be async

## Warnings (228)

11. [warning] no-cascading-set-state — kitchen-sink/src/examples/sierpinski/index.tsx:27
3 setState calls in a single useEffect — consider using useReducer or deriving state

12. [warning] click-events-have-key-events — kitchen-sink/src/examples/e2e-fixture/index.tsx:46
Enforce a clickable non-interactive element has at least one keyboard event listener.

13. [warning] no-static-element-interactions — kitchen-sink/src/examples/e2e-fixture/index.tsx:46
Static HTML elements with event handlers require a role.

14. [warning] no-react19-deprecated-apis — kitchen-sink/src/examples/e2e-fixture/index.tsx:1
useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

15. [warning] no-uncontrolled-input — kitchen-sink/src/examples/todo-list/index.tsx:92
with no `onChange` or `readOnly` — React renders this as a silently read-only field

16. [warning] rerender-functional-setstate — kitchen-sink/src/examples/todo-list/index.tsx:88
setIndex(index + ...) — use functional update to avoid stale closures

17. [warning] iframe-has-title — kitchen-sink/src/main.tsx:39
Missing `title` attribute for the `iframe` element.

18. [warning] design-no-default-tailwind-palette — kitchen-sink/src/main.tsx:19
border-gray-950 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

19. [warning] design-no-bold-heading — kitchen-sink/src/main.tsx:20
font-bold on

crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)

20. [warning] design-no-default-tailwind-palette — kitchen-sink/src/main.tsx:24
border-gray-950 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

21. [warning] design-no-default-tailwind-palette — kitchen-sink/src/main.tsx:29
border-gray-950 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

22. [warning] js-batch-dom-css — packages/extension/src/inject/notification.ts:36
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

23. [warning] js-batch-dom-css — packages/extension/src/inject/notification.ts:130
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

24. [warning] js-set-map-lookups — packages/scan/src/core/index.ts:299
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

25. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:172
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

26. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:176
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

27. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:177
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

28. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:178
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

29. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:179
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

30. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:195
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

31. [warning] no-pure-black-background — packages/scan/src/web/toolbar.tsx:22
Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950)

32. [warning] no-pure-black-background — packages/scan/src/web/toolbar.tsx:27
Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950)

33. [warning] no-giant-component — packages/scan/src/web/views/inspector/components-tree/index.tsx:386
Component "ComponentsTree" is 792 lines — consider breaking it into smaller focused components

34. [warning] design-no-redundant-size-axes — packages/scan/src/web/views/inspector/components-tree/index.tsx:1072
w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

35. [warning] design-no-redundant-size-axes — packages/scan/src/web/views/inspector/components-tree/index.tsx:1086
w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

36. [warning] design-no-redundant-size-axes — packages/scan/src/web/views/inspector/components-tree/index.tsx:1102
w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

37. [warning] async-await-in-loop — packages/scan/src/new-outlines/index.ts:153
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

38. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:304
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

39. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:305
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

40. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:306
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

41. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:307
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

42. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:318
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

43. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:381
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

44. [warning] client-passive-event-listeners — packages/scan/src/new-outlines/index.ts:407
"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())

45. [warning] rerender-transitions-scroll — packages/scan/src/new-outlines/index.ts:411
setState in a "scroll" handler triggers re-renders at scroll/pointer frequency — wrap in startTransition (mark as non-urgent), use useDeferredValue, or stash in a ref + rAF throttle

46. [warning] js-flatmap-filter — packages/scan/src/web/views/inspector/timeline/utils.ts:148
.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass

47. [warning] no-cascading-set-state — packages/scan/src/web/views/notifications/collapsed-event.tsx:36
11 setState calls in a single useEffect — consider using useReducer or deriving state

48. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1357
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

49. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1394
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

50. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1446
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

51. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1865
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

52. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1888
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

53. [warning] js-tosorted-immutable — packages/scan/src/polyfills.ts:4
[...array].sort() — use array.toSorted() for immutable sorting (ES2023)

54. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:5
Unknown property found

55. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:5
Unknown property found

56. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:5
Unknown property found

57. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:18
Unknown property found

58. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:18
Unknown property found

59. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:18
Unknown property found

60. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:23
Unknown property found

61. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:23
Unknown property found

62. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:23
Unknown property found

63. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:27
Unknown property found

64. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:27
Unknown property found

65. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:27
Unknown property found

66. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:31
Unknown property found

67. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:31
Unknown property found

68. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:31
Unknown property found

69. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:36
Unknown property found

70. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:36
Unknown property found

71. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:36
Unknown property found

72. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:45
Unknown property found

73. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:45
Unknown property found

74. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:45
Unknown property found

75. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:51
Unknown property found

76. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:51
Unknown property found

77. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:51
Unknown property found

78. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:56
Unknown property found

79. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:56
Unknown property found

80. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:56
Unknown property found

81. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:60
Unknown property found

82. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:60
Unknown property found

83. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:60
Unknown property found

84. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:64
Unknown property found

85. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:64
Unknown property found

86. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:64
Unknown property found

87. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:72
Unknown property found

88. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:72
Unknown property found

89. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:72
Unknown property found

90. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:78
Unknown property found

91. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:78
Unknown property found

92. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:78
Unknown property found

93. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:84
Unknown property found

94. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:84
Unknown property found

95. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:84
Unknown property found

96. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:89
Unknown property found

97. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:89
Unknown property found

98. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:89
Unknown property found

99. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:94
Unknown property found

100. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:94
Unknown property found

101. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:94
Unknown property found

102. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:99
Unknown property found

103. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:99
Unknown property found

104. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:99
Unknown property found

105. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:104
Unknown property found

106. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:104
Unknown property found

107. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:104
Unknown property found

108. [warning] js-cache-property-access — packages/scan/src/web/views/inspector/whats-changed/use-change-store.ts:123
aggregatedChanges.contextChanges.set is read 3 times inside this loop — hoist into a const at the top of the loop body

109. [warning] js-cache-property-access — packages/scan/src/web/views/inspector/whats-changed/use-change-store.ts:128
change.contextType.toString is read 3 times inside this loop — hoist into a const at the top of the loop body

110. [warning] design-no-default-tailwind-palette — packages/scan/src/web/views/inspector/diff-value.tsx:55
text-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

111. [warning] design-no-default-tailwind-palette — packages/scan/src/web/views/inspector/diff-value.tsx:81
text-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

112. [warning] design-no-default-tailwind-palette — packages/scan/src/web/views/inspector/diff-value.tsx:118
text-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

113. [warning] no-array-index-as-key — packages/scan/src/web/views/inspector/diff-value.tsx:143
Array index "index" used as key — causes bugs when list is reordered or filtered

114. [warning] js-flatmap-filter — packages/scan/src/core/notifications/event-tracking.ts:290
.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass

115. [warning] no-giant-component — packages/scan/src/web/views/inspector/overlay/index.tsx:48
Component "ScanOverlay" is 684 lines — consider breaking it into smaller focused components

116. [warning] no-cascading-set-state — packages/scan/src/web/views/inspector/what-changed.tsx:36
3 setState calls in a single useEffect — consider using useReducer or deriving state

117. [warning] js-combine-iterations — packages/scan/src/web/views/inspector/what-changed.tsx:49
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

118. [warning] no-render-in-render — packages/scan/src/web/views/inspector/what-changed.tsx:323
Inline render function "renderName()" — extract to a separate component for proper reconciliation

119. [warning] no-array-index-as-key — packages/scan/src/web/views/inspector/what-changed.tsx:452
Array index "i" used as key — causes bugs when list is reordered or filtered

120. [warning] js-batch-dom-css — packages/scan/src/web/views/inspector/flash-overlay.ts:64
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

121. [warning] js-batch-dom-css — packages/scan/src/web/views/inspector/flash-overlay.ts:68
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

122. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:18
Unknown property found

123. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:19
Unknown property found

124. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:20
Unknown property found

125. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:78
Unknown property found

126. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:79
Unknown property found

127. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:80
Unknown property found

128. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:123
Unknown property found

129. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:124
Unknown property found

130. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:125
Unknown property found

131. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:143
Unknown property found

132. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:144
Unknown property found

133. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:145
Unknown property found

134. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:165
Unknown property found

135. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:166
Unknown property found

136. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:167
Unknown property found

137. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:192
Unknown property found

138. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:193
Unknown property found

139. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:194
Unknown property found

140. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:213
Unknown property found

141. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:214
Unknown property found

142. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:215
Unknown property found

143. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:237
Unknown property found

144. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:238
Unknown property found

145. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:239
Unknown property found

146. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:265
Unknown property found

147. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:266
Unknown property found

148. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:267
Unknown property found

149. [warning] rerender-memo-with-default-value — packages/scan/src/web/views/notifications/icons.tsx:55
Default prop value [] creates a new array reference every render — extract to a module-level constant

150. [warning] no-unknown-property — packages/scan/src/web/widget/resize-handle.tsx:349
Unknown property found

151. [warning] no-giant-component — packages/scan/src/web/widget/resize-handle.tsx:26
Component "ResizeHandle" is 353 lines — consider breaking it into smaller focused components

152. [warning] rerender-lazy-state-init — packages/scan/src/web/views/notifications/render-explanation.tsx:16
useState(getIsProduction()) calls initializer on every render — use useState(() => getIsProduction()) for lazy initialization

153. [warning] no-cascading-set-state — packages/scan/src/web/views/notifications/popover.tsx:75
4 setState calls in a single useEffect — consider using useReducer or deriving state

154. [warning] no-permanent-will-change — packages/scan/src/web/views/notifications/popover.tsx:167
Permanent will-change wastes GPU memory — apply only during active animation and remove after

155. [warning] rerender-lazy-state-init — packages/scan/src/web/views/notifications/render-bar-chart.tsx:67
useState(getIsProduction()) calls initializer on every render — use useState(() => getIsProduction()) for lazy initialization

156. [warning] no-giant-component — packages/scan/src/web/views/notifications/render-bar-chart.tsx:184
Component "RenderBar" is 345 lines — consider breaking it into smaller focused components

157. [warning] rendering-hydration-mismatch-time — packages/scan/src/web/views/notifications/render-bar-chart.tsx:261
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

158. [warning] rendering-hydration-mismatch-time — packages/scan/src/web/views/notifications/render-bar-chart.tsx:365
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

159. [warning] no-array-index-as-key — packages/scan/src/web/views/notifications/render-bar-chart.tsx:502
Array index "i" used as key — causes bugs when list is reordered or filtered

160. [warning] js-combine-iterations — packages/scan/src/web/views/toolbar/index.tsx:194
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

161. [warning] js-combine-iterations — packages/scan/src/web/views/notifications/notifications.tsx:34
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

162. [warning] js-combine-iterations — packages/scan/src/web/views/notifications/notifications.tsx:43
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

163. [warning] js-combine-iterations — packages/scan/src/web/views/notifications/notifications.tsx:51
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

164. [warning] rendering-hydration-mismatch-time — packages/scan/src/web/views/notifications/notifications.tsx:441
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

165. [warning] rendering-hydration-mismatch-time — packages/scan/src/web/views/notifications/notifications.tsx:464
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

166. [warning] no-giant-component — packages/scan/src/web/widget/index.tsx:44
Component "Widget" is 715 lines — consider breaking it into smaller focused components

167. [warning] rerender-state-only-in-handlers — packages/scan/src/web/widget/index.tsx:653
useState "_" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

168. [warning] js-batch-dom-css — packages/scan/src/web/widget/index.tsx:574
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

169. [warning] js-batch-dom-css — packages/scan/src/web/widget/index.tsx:606
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

170. [warning] js-batch-dom-css — packages/scan/src/web/widget/index.tsx:607
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

171. [warning] rendering-hydration-no-flicker — packages/scan/src/web/widget/index.tsx:654
useEffect(setState, []) on mount causes a flash — consider useSyncExternalStore or suppressHydrationWarning

172. [warning] no-cascading-set-state — packages/scan/src/web/views/notifications/slowdown-history.tsx:27
3 setState calls in a single useEffect — consider using useReducer or deriving state

173. [warning] no-cascading-set-state — packages/scan/src/web/views/notifications/slowdown-history.tsx:59
3 setState calls in a single useEffect — consider using useReducer or deriving state

174. [warning] async-await-in-loop — packages/scan/tsup.config.ts:20
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

175. [warning] async-parallel — packages/scan/tsup.config.ts:144
3 sequential await statements that appear independent — use Promise.all() for parallel execution

176. [warning] server-fetch-without-revalidate — packages/website/app/api/waitlist/route.ts:38
fetch("https://app.loops.so/api/v1/contacts/create") in a Server Component / route handler defaults to forever-caching — pass { next: { revalidate: } } / { next: { tags: [...] } } / { cache: "no-store" } so stale data doesn't quietly persist

177. [warning] no-unknown-property — packages/website/app/replay/page.tsx:183
Unknown property found

178. [warning] click-events-have-key-events — packages/website/app/replay/page.tsx:136
Enforce a clickable non-interactive element has at least one keyboard event listener.

179. [warning] click-events-have-key-events — packages/website/app/replay/page.tsx:162
Enforce a clickable non-interactive element has at least one keyboard event listener.

180. [warning] click-events-have-key-events — packages/website/app/replay/page.tsx:172
Enforce a clickable non-interactive element has at least one keyboard event listener.

181. [warning] no-static-element-interactions — packages/website/app/replay/page.tsx:136
Static HTML elements with event handlers require a role.

182. [warning] no-static-element-interactions — packages/website/app/replay/page.tsx:162
Static HTML elements with event handlers require a role.

183. [warning] no-static-element-interactions — packages/website/app/replay/page.tsx:172
Static HTML elements with event handlers require a role.

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

185. [warning] prefer-useReducer — packages/website/app/replay/page.tsx:5
Component "ReplayPage" has 6 useState calls — consider useReducer for related state

186. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:86
border-gray-200 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

187. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:86
text-gray-900 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

188. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:86
placeholder-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

189. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:99
w-5 h-5 → use the shorthand size-5 (Tailwind v3.4+)

190. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:111
w-5 h-5 → use the shorthand size-5 (Tailwind v3.4+)

191. [warning] no-inline-bounce-easing — packages/website/app/replay/page.tsx:115
animate-bounce feels dated and tacky — use a subtle ease-out transform for natural deceleration

192. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:123
w-5 h-5 → use the shorthand size-5 (Tailwind v3.4+)

193. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:132
text-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

194. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:148
w-16 h-16 → use the shorthand size-16 (Tailwind v3.4+)

195. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:149
text-gray-900 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

196. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:149
w-7 h-7 → use the shorthand size-7 (Tailwind v3.4+)

197. [warning] no-pure-black-background — packages/website/app/replay/page.tsx:201
Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950)

198. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:205
w-7 h-7 → use the shorthand size-7 (Tailwind v3.4+)

199. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:207
w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

200. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:215
w-8 h-8 → use the shorthand size-8 (Tailwind v3.4+)

201. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:217
w-5 h-5 → use the shorthand size-5 (Tailwind v3.4+)

202. [warning] no-pure-black-background — packages/website/app/replay/page.tsx:222
Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950)

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

204. [warning] design-no-redundant-size-axes — packages/website/app/page.tsx:36
w-[18px] h-[18px] → use the shorthand size-[18px] (Tailwind v3.4+)

205. [warning] design-no-redundant-size-axes — packages/website/app/page.tsx:45
w-[18px] h-[18px] → use the shorthand size-[18px] (Tailwind v3.4+)

206. [warning] no-danger — packages/website/components/install-guide.tsx:260
Do not use `dangerouslySetInnerHTML` prop

207. [warning] no-danger — packages/website/components/install-guide.tsx:284
Do not use `dangerouslySetInnerHTML` prop

208. [warning] rendering-svg-precision — packages/website/components/icons/icon-discord.tsx:15
SVG d attribute uses 4+ decimal precision — truncate to 1–2 decimals to shrink markup with no visible difference

209. [warning] design-no-redundant-size-axes — packages/website/components/header.tsx:43
w-6 h-6 → use the shorthand size-6 (Tailwind v3.4+)

210. [warning] js-tosorted-immutable — packages/website/public/auto.global.js:2
[...array].sort() — use array.toSorted() for immutable sorting (ES2023)

211. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

212. [warning] client-passive-event-listeners — packages/website/public/auto.global.js:2
"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())

213. [warning] client-passive-event-listeners — packages/website/public/auto.global.js:2
"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())

214. [warning] js-cache-property-access — packages/website/public/auto.global.js:2
t.contextChanges.set is read 3 times inside this loop — hoist into a const at the top of the loop body

215. [warning] js-cache-property-access — packages/website/public/auto.global.js:2
n.contextType.toString is read 3 times inside this loop — hoist into a const at the top of the loop body

216. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

217. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

218. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

219. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

220. [warning] js-tosorted-immutable — packages/website/public/auto.global.js:2
[...array].sort() — use array.toSorted() for immutable sorting (ES2023)

221. [warning] async-await-in-loop — packages/website/public/auto.global.js:2
await inside a for-loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently

222. [warning] client-passive-event-listeners — packages/website/public/auto.global.js:2
"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())

223. [warning] rerender-transitions-scroll — packages/website/public/auto.global.js:2
setState in a "scroll" handler triggers re-renders at scroll/pointer frequency — wrap in startTransition (mark as non-urgent), use useDeferredValue, or stash in a ref + rAF throttle

224. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

225. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

226. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

227. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

228. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.map().filter() iterates the array twice — combine into a single loop with .reduce() or for...of

229. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

230. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

231. [warning] js-flatmap-filter — packages/website/public/auto.global.js:2
.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass

232. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().forEach() iterates the array twice — combine into a single loop with .reduce() or for...of

233. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

234. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

235. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

236. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

237. [warning] client-passive-event-listeners — packages/website/public/auto.global.js:2
"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())

238. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups
```

---

### ❌ Errors (10)

Async client component "B" — client components cannot be async · 5 in 1 file

nextjs-async-client-component

> Fetch data in a parent Server Component and pass it as props, or use useQuery/useSWR in the client component

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2, 2, 2, 2 |

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(...) · 3 in 2 files

effect-needs-cleanup

> 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

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/slowdown-history.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/slowdown-history.tsx#L27) | 27, 339 |
| [`packages/scan/src/web/views/notifications/notifications.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/notifications.tsx#L227) | 227 |

Missing "key" prop for element in iterator. · 1 in 1 file

jsx-key

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

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/collapsed-event.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/collapsed-event.tsx#L165) | 165 |

"triggerRef.current" in deps — refs are mutable and don't trigger re-renders, so React won't re-run this effect when it changes. Read the ref inside the effect body instead · 1 in 1 file

no-mutable-in-deps

> Read mutable values (`location.pathname`, `ref.current`) inside the effect body instead of in the deps array, or subscribe with `useSyncExternalStore`. Mutations to these don't trigger re-renders, so listing them in deps doesn't make the effect react to changes

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/popover.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/popover.tsx#L73) | 73 |

---

### ⚠️ Warnings (228)

Unknown property found · 83 in 4 files

no-unknown-property

> Use 'strokeWidth' instead

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/components/svg-sprite/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/components/svg-sprite/index.tsx#L5) | 5, 5, 5, 18, 18, 18, 23, 23, +46 more |
| [`packages/scan/src/web/views/notifications/icons.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/icons.tsx#L18) | 18, 19, 20, 78, 79, 80, 123, 124, +19 more |
| [`packages/scan/src/web/widget/resize-handle.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/widget/resize-handle.tsx#L349) | 349 |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L183) | 183 |

Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows · 19 in 5 files

js-batch-dom-css

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

| File | Lines |
| --- | --- |
| [`packages/scan/src/core/notifications/outline-overlay.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/core/notifications/outline-overlay.ts#L172) | 172, 176, 177, 178, 179, 195 |
| [`packages/scan/src/new-outlines/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/new-outlines/index.ts#L304) | 304, 305, 306, 307, 318, 381 |
| [`packages/scan/src/web/widget/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/widget/index.tsx#L574) | 574, 606, 607 |
| [`packages/extension/src/inject/notification.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/extension/src/inject/notification.ts#L36) | 36, 130 |
| [`packages/scan/src/web/views/inspector/flash-overlay.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/flash-overlay.ts#L64) | 64, 68 |

array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups · 17 in 3 files

js-set-map-lookups

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

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2, 2, 2, 2, 2, 2, 2, +3 more |
| [`packages/scan/src/web/views/inspector/utils.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/utils.ts#L1357) | 1357, 1394, 1446, 1865, 1888 |
| [`packages/scan/src/core/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/core/index.ts#L299) | 299 |

w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+) · 15 in 4 files

design-no-redundant-size-axes

> Collapse `w-N h-N` to `size-N` (Tailwind v3.4+) when both axes match

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L99) | 99, 111, 123, 148, 149, 205, 207, 215, +1 more |
| [`packages/scan/src/web/views/inspector/components-tree/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/components-tree/index.tsx#L1072) | 1072, 1086, 1102 |
| [`packages/website/app/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/page.tsx#L36) | 36, 45 |
| [`packages/website/components/header.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/components/header.tsx#L43) | 43 |

.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of · 12 in 4 files

js-combine-iterations

> Combine `.map().filter()` (or similar chains) into a single pass with `.reduce()` or a `for...of` loop to avoid iterating the array twice

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2, 2, 2, 2, 2, 2 |
| [`packages/scan/src/web/views/notifications/notifications.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/notifications.tsx#L34) | 34, 43, 51 |
| [`packages/scan/src/web/views/inspector/what-changed.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/what-changed.tsx#L49) | 49 |
| [`packages/scan/src/web/views/toolbar/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/toolbar/index.tsx#L194) | 194 |

border-gray-950 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest) · 11 in 3 files

design-no-default-tailwind-palette

> Replace `indigo-*` / `gray-*` / `slate-*` with project tokens, your brand color, or a less-default neutral (`zinc`, `neutral`, `stone`)

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L86) | 86, 86, 86, 132, 149 |
| [`kitchen-sink/src/main.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/kitchen-sink/src/main.tsx#L19) | 19, 24, 29 |
| [`packages/scan/src/web/views/inspector/diff-value.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/diff-value.tsx#L55) | 55, 81, 118 |

3 setState calls in a single useEffect — consider using useReducer or deriving state · 6 in 5 files

no-cascading-set-state

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

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/slowdown-history.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/slowdown-history.tsx#L27) | 27, 59 |
| [`kitchen-sink/src/examples/sierpinski/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/kitchen-sink/src/examples/sierpinski/index.tsx#L27) | 27 |
| [`packages/scan/src/web/views/notifications/collapsed-event.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/collapsed-event.tsx#L36) | 36 |
| [`packages/scan/src/web/views/inspector/what-changed.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/what-changed.tsx#L36) | 36 |
| [`packages/scan/src/web/views/notifications/popover.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/popover.tsx#L75) | 75 |

Component "ComponentsTree" is 792 lines — consider breaking it into smaller focused components · 5 in 5 files

no-giant-component

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

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/inspector/components-tree/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/components-tree/index.tsx#L386) | 386 |
| [`packages/scan/src/web/views/inspector/overlay/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/overlay/index.tsx#L48) | 48 |
| [`packages/scan/src/web/widget/resize-handle.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/widget/resize-handle.tsx#L26) | 26 |
| [`packages/scan/src/web/views/notifications/render-bar-chart.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-bar-chart.tsx#L184) | 184 |
| [`packages/scan/src/web/widget/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/widget/index.tsx#L44) | 44 |

"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()) · 5 in 2 files

client-passive-event-listeners

> 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.

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2, 2, 2 |
| [`packages/scan/src/new-outlines/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/new-outlines/index.ts#L407) | 407 |

Enforce a clickable non-interactive element has at least one keyboard event listener. · 4 in 2 files

click-events-have-key-events

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

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L136) | 136, 162, 172 |
| [`kitchen-sink/src/examples/e2e-fixture/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/kitchen-sink/src/examples/e2e-fixture/index.tsx#L46) | 46 |

Static HTML elements with event handlers require a role. · 4 in 2 files

no-static-element-interactions

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

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L136) | 136, 162, 172 |
| [`kitchen-sink/src/examples/e2e-fixture/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/kitchen-sink/src/examples/e2e-fixture/index.tsx#L46) | 46 |

Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950) · 4 in 2 files

no-pure-black-background

> Tint the background slightly toward your brand hue — e.g. `#0a0a0f` or Tailwind's `bg-gray-950`. Pure black looks harsh on modern displays

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/toolbar.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/toolbar.tsx#L22) | 22, 27 |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L201) | 201, 222 |

aggregatedChanges.contextChanges.set is read 3 times inside this loop — hoist into a const at the top of the loop body · 4 in 2 files

js-cache-property-access

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

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/inspector/whats-changed/use-change-store.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/whats-changed/use-change-store.ts#L123) | 123, 128 |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2 |

Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional · 4 in 2 files

rendering-hydration-mismatch-time

> Wrap dynamic time/random values in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/render-bar-chart.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-bar-chart.tsx#L261) | 261, 365 |
| [`packages/scan/src/web/views/notifications/notifications.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/notifications.tsx#L441) | 441, 464 |

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 · 3 in 3 files

async-await-in-loop

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

| File | Lines |
| --- | --- |
| [`packages/scan/src/new-outlines/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/new-outlines/index.ts#L153) | 153 |
| [`packages/scan/tsup.config.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/tsup.config.ts#L20) | 20 |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2 |

.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass · 3 in 3 files

js-flatmap-filter

> Use `.flatMap(item => condition ? [value] : [])` — transforms and filters in a single pass instead of creating an intermediate array

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/inspector/timeline/utils.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/timeline/utils.ts#L148) | 148 |
| [`packages/scan/src/core/notifications/event-tracking.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/core/notifications/event-tracking.ts#L290) | 290 |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2 |

[...array].sort() — use array.toSorted() for immutable sorting (ES2023) · 3 in 2 files

js-tosorted-immutable

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

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2 |
| [`packages/scan/src/polyfills.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/polyfills.ts#L4) | 4 |

Array index "index" used as key — causes bugs when list is reordered or filtered · 3 in 3 files

no-array-index-as-key

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

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/inspector/diff-value.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/diff-value.tsx#L143) | 143 |
| [`packages/scan/src/web/views/inspector/what-changed.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/what-changed.tsx#L452) | 452 |
| [`packages/scan/src/web/views/notifications/render-bar-chart.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-bar-chart.tsx#L502) | 502 |

setState in a "scroll" handler triggers re-renders at scroll/pointer frequency — wrap in startTransition (mark as non-urgent), use useDeferredValue, or stash in a ref + rAF throttle · 2 in 2 files

rerender-transitions-scroll

> Wrap the setState in startTransition (mark as non-urgent), use useDeferredValue, or stash in a ref + rAF throttle so scroll/pointer events don't trigger a re-render per fire

| File | Lines |
| --- | --- |
| [`packages/scan/src/new-outlines/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/new-outlines/index.ts#L411) | 411 |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2 |

useState(getIsProduction()) calls initializer on every render — use useState(() => getIsProduction()) for lazy initialization · 2 in 2 files

rerender-lazy-state-init

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

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/render-explanation.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-explanation.tsx#L16) | 16 |
| [`packages/scan/src/web/views/notifications/render-bar-chart.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-bar-chart.tsx#L67) | 67 |

Page without metadata or generateMetadata export — hurts SEO · 2 in 2 files

nextjs-missing-metadata

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

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L1) | 1 |
| [`packages/website/app/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/page.tsx#L1) | 1 |

Do not use `dangerouslySetInnerHTML` prop · 2 in 1 file

no-danger

> `dangerouslySetInnerHTML` is a way to inject HTML into your React component. This is dangerous because it can easily lead to XSS vulnerabilities.

| File | Lines |
| --- | --- |
| [`packages/website/components/install-guide.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/components/install-guide.tsx#L260) | 260, 284 |

useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'` · 1 in 1 file

no-react19-deprecated-apis

> 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+.

| File | Lines |
| --- | --- |
| [`kitchen-sink/sr

---

_Issue body truncated. See the full report at [react.review/dashboard](https://react.review/dashboard)._

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.