You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
improvement(files): react-doctor performance pass on the files module (#5330)
* improvement(files): react-doctor performance pass on the files module
* fix(files): use spread-sort not toSorted in client (Safari<16/iOS15 crash)
SWC does not polyfill Array.prototype.toSorted and the repo sets no
core-js/browserslist target, so it throws on Safari <16 / iOS 15. Revert
the two client-side toSorted calls to [...arr].sort() and correct the
harness guidance in sim-components.md accordingly.
Copy file name to clipboardExpand all lines: .claude/rules/sim-components.md
+18Lines changed: 18 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,3 +12,21 @@ Component authoring rules — structure order (refs → external hooks → store
12
12
-`'use client'` only when using React hooks or browser-only APIs.
13
13
- Prefer semantic HTML (`aside`, `nav`, `article`).
14
14
- Optional-chain callbacks: `onAction?.(id)`.
15
+
16
+
## List-render performance
17
+
18
+
When rendering or sorting a list of rows against a lookup collection (members, folders, tags), keep the per-row work O(1):
19
+
20
+
-**Precompute a lookup `Map` once**, never `array.find(...)` per row. Build `const byId = useMemo(() => { const m = new Map<string, T>(); for (const x of items ?? []) m.set(x.id, x); return m }, [items])` and read `byId.get(id)` in the sort comparator, `.map(...)`, and cell builders. A `.find` inside a sort comparator is O(n²·log n) — the worst offender. Depend memos on the derived `Map`, not the raw array.
21
+
-**Sort with `[...array].sort(cmp)` in client code — NOT `array.toSorted(cmp)`.** SWC (Next's compiler) transforms syntax but does not polyfill prototype methods, and the repo sets no core-js/browserslist target, so `toSorted`/`toReversed`/`toSpliced`/`Array.prototype.with` ship as-is and throw `TypeError` on Safari <16 / iOS 15 (they landed in Safari 16). `tsconfig``lib: ES2023` only affects type-checking, never the runtime. The `[...arr].sort()` spread copy is the intended cost — it keeps the sort non-mutating without an unpolyfilled builtin. These methods are only safe in server-only modules (no `'use client'`, executed on Node ≥20). react-doctor's `js-tosorted-immutable` is therefore a won't-fix in client components.
22
+
-**Partition in a single pass** — when splitting one collection into several (`fileIds`/`folderIds`), do one `for…of` pushing into each bucket and return `{ a, b }` from a single `useMemo`, not two memos that each `map→filter→map` the same source twice.
23
+
24
+
## react-doctor (`npx react-doctor`) — apply the wins, skip the false positives
25
+
26
+
react-doctor diagnostics are hypotheses, not verdicts — confirm against the code before acting, and preserve behavior. Known repo-specific false positives to NOT "fix":
27
+
28
+
-`no-barrel-import` — barrel imports are the repo convention (see sim-imports.md, "Barrel Exports"). Keep them.
29
+
-`js-tosorted-immutable` — in `'use client'` code, keep `[...arr].sort(cmp)`; `toSorted` is unpolyfilled and crashes Safari <16 / iOS 15 (see "List-render performance" above). Only apply it in server-only modules.
30
+
-`rerender-state-only-in-handlers` / "state set but never rendered" — a false positive when the `useState` is consumed by a `useEffect`/`useLayoutEffect` dependency (the effect must re-run on change). Only convert to a ref when nothing reads the value reactively.
31
+
-`async-await-in-loop` on an upload/progress loop where sequential execution is intentional (per-item progress, server backpressure) — leave it.
32
+
- Broad refactors (`prefer-useReducer` for many `useState`, `no-giant-component` splits) — out of scope for a perf pass; note, don't churn.
0 commit comments