Skip to content

React Review Audit #1

Description

@react-doctor
2 errors, ⚠️ 67 warnings 84 score
Copy as prompt
Fix the following React Review diagnostics in my codebase.

## Errors (2)

1. [error] rules-of-hooks — packages/openstory/tests/renderers/react.test.ts:26
   React Hook "useState" is called in function "Anonymous" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

2. [error] rules-of-hooks — packages/openstory/tests/renderers/react.test.ts:48
   React Hook "useState" is called in function "Anonymous" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".

## Warnings (67)

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

4. [warning] design-no-redundant-size-axes — packages/openstory/src/shell/src/components/top-bar.tsx:45
   w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

5. [warning] no-children-prop — packages/openstory/src/react/renderer.ts:104
   Avoid passing children using a prop.

6. [warning] async-defer-await — packages/openstory/src/react/renderer.ts:124
   await blocks the function before an early-return that doesn't use the awaited value — move the await after the synchronous guard so the skip path stays fast

7. [warning] js-set-map-lookups — packages/openstory/src/boot/boot.ts:120
   array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

8. [warning] anchor-is-valid — packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/composite-dashboard.stories.tsx:106
   Use of incorrect `href` for the 'a' element.

9. [warning] no-inline-exhaustive-style — packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/composite-dashboard.stories.tsx:109
   9 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

10. [warning] no-inline-exhaustive-style — packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/composite-dashboard.stories.tsx:129
   8 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

11. [warning] no-inline-exhaustive-style — packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/composite-dashboard.stories.tsx:233
   8 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

12. [warning] design-no-redundant-size-axes — packages/openstory/src/shell/src/components/canvas.tsx:39
   w-3 h-3 → use the shorthand size-3 (Tailwind v3.4+)

13. [warning] js-set-map-lookups — packages/openstory/src/shell/src/lib/url-state.ts:25
   array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

14. [warning] anchor-is-valid — packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx:163
   Use of incorrect `href` for the 'a' element.

15. [warning] anchor-is-valid — packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx:172
   Use of incorrect `href` for the 'a' element.

16. [warning] label-has-associated-control — packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx:250
   A form label must be associated with a control.

17. [warning] no-many-boolean-props — packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx:56
   Component "Scene" takes 5 boolean-like props (showContextMenu, isPromptMode, isPendingDismiss…) — consider compound components or explicit variants instead of stacking flags

18. [warning] no-inline-exhaustive-style — packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx:222
   8 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

19. [warning] no-inline-exhaustive-style — packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx:266
   8 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

20. [warning] no-outline-none — packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx:274
   outline: none removes keyboard focus visibility — use :focus-visible styling instead, or provide a box-shadow focus ring

21. [warning] no-inline-exhaustive-style — packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx:281
   8 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

22. [warning] no-cascading-set-state — packages/openstory/src/shell/src/state/use-manifest.ts:26
   5 setState calls in a single useEffect — consider using useReducer or deriving state

23. [warning] js-length-check-first — packages/openstory/src/utils/deep-equal.ts:8
   .every() over an array compared to another array — short-circuit with `a.length === b.length && a.every(...)` so unequal-length arrays exit immediately

24. [warning] server-sequential-independent-await — packages/openstory/src/plugin/manifest.ts:147
   Sequential `await` without a data dependency on the previous result — wrap the independent calls in `Promise.all([...])` so they race instead of waterfalling

25. [warning] async-await-in-loop — packages/openstory/src/plugin/manifest.ts:114
   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

26. [warning] async-await-in-loop — packages/openstory/src/utils/extract-props.ts:263
   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

27. [warning] async-await-in-loop — packages/openstory/src/utils/extract-props.ts:286
   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

28. [warning] async-await-in-loop — packages/openstory/src/utils/extract-props.ts:778
   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

29. [warning] async-await-in-loop — packages/openstory/src/utils/extract-props.ts:800
   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

30. [warning] async-await-in-loop — packages/openstory/src/utils/extract-props.ts:811
   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

31. [warning] async-await-in-loop — packages/openstory/src/utils/extract-props.ts:1493
   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

32. [warning] js-index-maps — packages/openstory/src/utils/render-csf-story.ts:145
   array.find() in a loop is O(n*m) — build a Map for O(1) lookups

33. [warning] design-no-redundant-size-axes — packages/openstory/src/shell/src/components/ui/select.tsx:23
   w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

34. [warning] design-no-redundant-size-axes — packages/openstory/src/shell/src/components/ui/select.tsx:71
   w-3.5 h-3.5 → use the shorthand size-3.5 (Tailwind v3.4+)

35. [warning] design-no-redundant-size-axes — packages/openstory/src/shell/src/components/ui/select.tsx:73
   w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

36. [warning] no-cascading-set-state — packages/openstory/src/shell/src/state/use-iframe-comms.ts:32
   3 setState calls in a single useEffect — consider using useReducer or deriving state

37. [warning] no-cascading-set-state — packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/bouncing-timer.react.tsx:33
   3 setState calls in a single useEffect — consider using useReducer or deriving state

38. [warning] no-inline-exhaustive-style — packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/bouncing-timer.react.tsx:86
   10 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

39. [warning] rerender-lazy-state-init — packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/live-counter.react.tsx:39
   useState(now()) calls initializer on every render — use useState(() => now()) for lazy initialization

40. [warning] no-cascading-set-state — packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/live-counter.react.tsx:47
   4 setState calls in a single useEffect — consider using useReducer or deriving state

41. [warning] no-inline-exhaustive-style — packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/live-counter.react.tsx:66
   8 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

42. [warning] no-barrel-import — packages/openstory/src/utils/build-inline-vite-config.ts:5
   Import from barrel/index file — import directly from "../plugin/plugin" for better tree-shaking

43. [warning] server-sequential-independent-await — packages/openstory/src/cli/build.ts:171
   Sequential `await` without a data dependency on the previous result — wrap the independent calls in `Promise.all([...])` so they race instead of waterfalling

44. [warning] async-parallel — packages/openstory/src/cli/build.ts:165
   3 sequential await statements that appear independent — use Promise.all() for parallel execution

45. [warning] async-await-in-loop — packages/openstory/src/cli/build.ts:226
   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

46. [warning] async-await-in-loop — packages/openstory/src/utils/generate-stories.ts:52
   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

47. [warning] js-tosorted-immutable — packages/openstory/src/utils/generate-stories.ts:65
   [...array].sort() — use array.toSorted() for immutable sorting (ES2023)

48. [warning] async-await-in-loop — packages/openstory/src/utils/generate-stories.ts:106
   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

49. [warning] server-sequential-independent-await — packages/openstory/src/cli/inspect.ts:36
   Sequential `await` without a data dependency on the previous result — wrap the independent calls in `Promise.all([...])` so they race instead of waterfalling

50. [warning] async-await-in-loop — packages/openstory/src/plugin/preview-discovery.ts:11
   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

51. [warning] async-await-in-loop — packages/openstory/src/utils/resolve-tsconfig-paths.ts:76
   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

52. [warning] server-sequential-independent-await — packages/openstory/src/cli/dev.ts:20
   Sequential `await` without a data dependency on the previous result — wrap the independent calls in `Promise.all([...])` so they race instead of waterfalling

53. [warning] async-parallel — packages/openstory/src/cli/dev.ts:14
   4 sequential await statements that appear independent — use Promise.all() for parallel execution

54. [warning] no-effect-chain — packages/openstory/src/shell/src/app.tsx:80
   useEffect reacts to "selectedStoryId" 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

55. [warning] prefer-useReducer — packages/openstory/src/shell/src/app.tsx:35
   Component "App" has 5 useState calls — consider useReducer for related state

56. [warning] js-tosorted-immutable — packages/openstory/src/cli/list.ts:26
   [...array].sort() — use array.toSorted() for immutable sorting (ES2023)

57. [warning] server-sequential-independent-await — packages/openstory/src/cli/list.ts:38
   Sequential `await` without a data dependency on the previous result — wrap the independent calls in `Promise.all([...])` so they race instead of waterfalling

58. [warning] no-inline-exhaustive-style — packages/openstory/tests/integration/fixtures/react-grab-like/stories/target-box.tsx:6
   15 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

59. [warning] server-sequential-independent-await — packages/openstory/tests/utils/extract-props.test.ts:178
   Sequential `await` without a data dependency on the previous result — wrap the independent calls in `Promise.all([...])` so they race instead of waterfalling

60. [warning] server-sequential-independent-await — packages/openstory/tests/utils/extract-props.test.ts:193
   Sequential `await` without a data dependency on the previous result — wrap the independent calls in `Promise.all([...])` so they race instead of waterfalling

61. [warning] async-await-in-loop — packages/openstory/tests/renderers/test-utils.ts:27
   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

62. [warning] async-await-in-loop — packages/openstory/tests/renderers/test-utils.ts:40
   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

63. [warning] js-combine-iterations — packages/openstory/tests/utils/generate-stories.test.ts:126
   .filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

64. [warning] no-barrel-import — packages/openstory/tests/integration/plugin-smoke.test.ts:8
   Import from barrel/index file — import directly from "../../src/plugin/plugin" for better tree-shaking

65. [warning] no-barrel-import — packages/openstory/tests/integration/react-grab-render.test.ts:16
   Import from barrel/index file — import directly from "../../src/plugin/plugin" for better tree-shaking

66. [warning] async-await-in-loop — packages/openstory/tests/integration/react-grab-render.test.ts:64
   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

67. [warning] async-defer-await — packages/openstory/tests/integration/react-grab-render.test.ts:95
   await blocks the function before an early-return that doesn't use the awaited value — move the await after the synchronous guard so the skip path stays fast

68. [warning] no-barrel-import — packages/openstory/tests/plugin/plugin-config-hook.test.ts:6
   Import from barrel/index file — import directly from "../../src/plugin/plugin" for better tree-shaking

69. [warning] async-await-in-loop — packages/openstory/tests/integration/parse-fixture.test.ts:33
   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

❌ Errors (2)

React Hook "useState" is called in function "Anonymous" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use". · 2 in 1 file

rules-of-hooks

File Lines
packages/openstory/tests/renderers/react.test.ts 26, 48

⚠️ Warnings (67)

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 · 16 in 9 files

async-await-in-loop

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

File Lines
packages/openstory/src/utils/extract-props.ts 263, 286, 778, 800, 811, 1493
packages/openstory/src/utils/generate-stories.ts 52, 106
packages/openstory/tests/renderers/test-utils.ts 27, 40
packages/openstory/src/plugin/manifest.ts 114
packages/openstory/src/cli/build.ts 226
…and 4 more files view on react.review
9 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse · 9 in 5 files

no-inline-exhaustive-style

Move styles to a CSS class, CSS module, Tailwind utilities, or a styled component — inline objects with many properties hurt readability and create new references every render

File Lines
packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/composite-dashboard.stories.tsx 109, 129, 233
packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx 222, 266, 281
packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/bouncing-timer.react.tsx 86
packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/live-counter.react.tsx 66
packages/openstory/tests/integration/fixtures/react-grab-like/stories/target-box.tsx 6
Sequential `await` without a data dependency on the previous result — wrap the independent calls in `Promise.all([...])` so they race instead of waterfalling · 7 in 6 files

server-sequential-independent-await

Wrap independent awaits in Promise.all([...]) so they race instead of waterfalling — second call doesn't depend on the first

File Lines
packages/openstory/tests/utils/extract-props.test.ts 178, 193
packages/openstory/src/plugin/manifest.ts 147
packages/openstory/src/cli/build.ts 171
packages/openstory/src/cli/inspect.ts 36
packages/openstory/src/cli/dev.ts 20
…and 1 more file view on react.review
w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+) · 5 in 3 files

design-no-redundant-size-axes

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

File Lines
packages/openstory/src/shell/src/components/ui/select.tsx 23, 71, 73
packages/openstory/src/shell/src/components/top-bar.tsx 45
packages/openstory/src/shell/src/components/canvas.tsx 39
5 setState calls in a single useEffect — consider using useReducer or deriving state · 4 in 4 files

no-cascading-set-state

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

File Lines
packages/openstory/src/shell/src/state/use-manifest.ts 26
packages/openstory/src/shell/src/state/use-iframe-comms.ts 32
packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/bouncing-timer.react.tsx 33
packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/live-counter.react.tsx 47
Import from barrel/index file — import directly from "../plugin/plugin" for better tree-shaking · 4 in 4 files

no-barrel-import

Import from the direct path: import { Button } from './components/Button' instead of ./components

File Lines
packages/openstory/src/utils/build-inline-vite-config.ts 5
packages/openstory/tests/integration/plugin-smoke.test.ts 8
packages/openstory/tests/integration/react-grab-render.test.ts 16
packages/openstory/tests/plugin/plugin-config-hook.test.ts 6
Use of incorrect `href` for the 'a' element. · 3 in 2 files

anchor-is-valid

Provide a correct href for the a element.

File Lines
packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx 163, 172
packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/composite-dashboard.stories.tsx 106
await blocks the function before an early-return that doesn't use the awaited value — move the await after the synchronous guard so the skip path stays fast · 2 in 2 files

async-defer-await

Move the await after the synchronous early-return guard so the skip path stays fast

File Lines
packages/openstory/src/react/renderer.ts 124
packages/openstory/tests/integration/react-grab-render.test.ts 95
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups · 2 in 2 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/openstory/src/boot/boot.ts 120
packages/openstory/src/shell/src/lib/url-state.ts 25
3 sequential await statements that appear independent — use Promise.all() for parallel execution · 2 in 2 files

async-parallel

Use const [a, b] = await Promise.all([fetchA(), fetchB()]) to run independent operations concurrently

File Lines
packages/openstory/src/cli/build.ts 165
packages/openstory/src/cli/dev.ts 14
[...array].sort() — use array.toSorted() for immutable sorting (ES2023) · 2 in 2 files

js-tosorted-immutable

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

File Lines
packages/openstory/src/utils/generate-stories.ts 65
packages/openstory/src/cli/list.ts 26
Page without metadata or generateMetadata export — hurts SEO · 1 in 1 file

nextjs-missing-metadata

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

File Lines
apps/website/src/app/page.tsx 1
Avoid passing children using a prop. · 1 in 1 file

no-children-prop

The canonical way to pass children in React is to use JSX elements

File Lines
packages/openstory/src/react/renderer.ts 104
A form label must be associated with a control. · 1 in 1 file

label-has-associated-control

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

File Lines
packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx 250
Component "Scene" takes 5 boolean-like props (showContextMenu, isPromptMode, isPendingDismiss…) — consider compound components or explicit variants instead of stacking flags · 1 in 1 file

no-many-boolean-props

Split into compound components or named variants: <Button.Primary />, <DialogConfirm /> instead of stacking isPrimary, isConfirm flags

File Lines
packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx 56
outline: none removes keyboard focus visibility — use :focus-visible styling instead, or provide a box-shadow focus ring · 1 in 1 file

no-outline-none

Use :focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px } to show focus only for keyboard users while hiding it for mouse clicks

File Lines
packages/openstory/tests/integration/fixtures/react-grab-like/stories/renderer.stories.tsx 274
.every() over an array compared to another array — short-circuit with `a.length === b.length && a.every(...)` so unequal-length arrays exit immediately · 1 in 1 file

js-length-check-first

Short-circuit with a.length === b.length && a.every((x, i) => x === b[i]) — unequal-length arrays exit immediately

File Lines
packages/openstory/src/utils/deep-equal.ts 8
array.find() in a loop is O(n*m) — build a Map for O(1) lookups · 1 in 1 file

js-index-maps

Build an index Map once outside the loop instead of array.find(...) inside it

File Lines
packages/openstory/src/utils/render-csf-story.ts 145
useState(now()) calls initializer on every render — use useState(() => now()) for lazy initialization · 1 in 1 file

rerender-lazy-state-init

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

File Lines
packages/openstory/tests/integration/fixtures/react-grab-like/stories/playground/live-counter.react.tsx 39
useEffect reacts to "selectedStoryId" 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 · 1 in 1 file

no-effect-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 effect link adds an extra render and makes the code rigid as requirements evolve

File Lines
packages/openstory/src/shell/src/app.tsx 80
Component "App" has 5 useState calls — consider useReducer for related state · 1 in 1 file

prefer-useReducer

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

File Lines
packages/openstory/src/shell/src/app.tsx 35
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of · 1 in 1 file

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/openstory/tests/utils/generate-stories.test.ts 126

Reviewed by reactreview for commit 9937de8. Configure here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions