ai-workflow-init 6.6.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +7 -0
- package/.claude/commands/audit-workflow.md +209 -0
- package/.claude/commands/cleanup.md +184 -0
- package/.claude/commands/create-plan.md +3 -63
- package/.claude/commands/execute-plan.md +3 -44
- package/.claude/output-styles/qna-consultant.md +237 -0
- package/.claude/skills/frontend-design-fundamentals/SKILL.md +11 -10
- package/.claude/skills/react-best-practices/SKILL.md +47 -437
- package/.claude/skills/react-best-practices/references/code-patterns.md +409 -0
- package/.claude/skills/skill-creator/SKILL.md +77 -290
- package/.claude/skills/skill-creator/references/creation-process.md +154 -0
- package/.claude/themes/README.md +37 -0
- package/.claude/themes/bold-vibrant.theme.json +81 -0
- package/.claude/themes/minimal-monochrome.theme.json +34 -94
- package/.claude/themes/professional-blue.theme.json +31 -89
- package/.claude/themes/warm-organic.theme.json +67 -0
- package/AGENTS.md +7 -0
- package/README.md +4 -88
- package/docs/ai/planning/epic-template.md +0 -7
- package/docs/ai/planning/feature-template.md +0 -15
- package/package.json +1 -1
- package/.claude/commands/beads-breakdown.md +0 -278
- package/.claude/commands/beads-create-epic-plan.md +0 -205
- package/.claude/commands/beads-done.md +0 -248
- package/.claude/commands/beads-next.md +0 -260
- package/.claude/commands/beads-status.md +0 -247
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: react-best-practices
|
|
3
3
|
description: |
|
|
4
|
-
React and Next.js performance optimization
|
|
5
|
-
Contains
|
|
4
|
+
React and Next.js performance optimization (NOT styling/visual design).
|
|
5
|
+
Contains rules for async patterns, bundle optimization, and rendering performance.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Writing new React components or Next.js pages
|
|
7
|
+
Use when working with React/Next.js performance:
|
|
9
8
|
- Implementing data fetching (client or server-side)
|
|
10
|
-
- Reviewing code for performance issues
|
|
11
|
-
- Refactoring existing React/Next.js code
|
|
12
9
|
- Optimizing bundle size or load times
|
|
13
10
|
- Working with async operations and Promise handling
|
|
14
11
|
- Implementing Suspense boundaries or streaming
|
|
12
|
+
- Reviewing code for performance issues
|
|
15
13
|
|
|
16
14
|
Keywords: React, Next.js, performance, optimization, bundle, async, Promise,
|
|
17
15
|
waterfall, rerender, memo, useMemo, useCallback, Suspense, RSC,
|
|
@@ -20,7 +18,8 @@ description: |
|
|
|
20
18
|
Do NOT load for:
|
|
21
19
|
- Non-React projects (Vue, Angular, Svelte, etc.)
|
|
22
20
|
- Backend-only code without React integration
|
|
23
|
-
- Pure CSS/styling work (use frontend-design-fundamentals)
|
|
21
|
+
- Pure CSS/styling/visual design work (use frontend-design-fundamentals)
|
|
22
|
+
- UI component styling, colors, spacing (use frontend-design-fundamentals)
|
|
24
23
|
|
|
25
24
|
Works with other skills:
|
|
26
25
|
- frontend-design-fundamentals: For UI/styling best practices
|
|
@@ -29,458 +28,67 @@ description: |
|
|
|
29
28
|
|
|
30
29
|
# React Best Practices
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Rule Categories by Priority
|
|
37
|
-
|
|
38
|
-
| Priority | Category | Impact | Prefix |
|
|
39
|
-
|----------|----------|--------|--------|
|
|
40
|
-
| 1 | Eliminating Waterfalls | CRITICAL | `async-` |
|
|
41
|
-
| 2 | Bundle Size Optimization | CRITICAL | `bundle-` |
|
|
42
|
-
| 3 | Server-Side Performance | HIGH | `server-` |
|
|
43
|
-
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | `client-` |
|
|
44
|
-
| 5 | Re-render Optimization | MEDIUM | `rerender-` |
|
|
45
|
-
| 6 | Rendering Performance | MEDIUM | `rendering-` |
|
|
46
|
-
| 7 | JavaScript Performance | LOW-MEDIUM | `js-` |
|
|
47
|
-
| 8 | Advanced Patterns | LOW | `advanced-` |
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## 1. Eliminating Waterfalls (CRITICAL)
|
|
52
|
-
|
|
53
|
-
Waterfalls are the #1 performance killer. Each sequential await adds full network latency.
|
|
54
|
-
|
|
55
|
-
### Promise.all() for Independent Operations
|
|
56
|
-
|
|
57
|
-
**Impact:** 2-10× improvement
|
|
58
|
-
|
|
59
|
-
When async operations have no interdependencies, execute them concurrently.
|
|
60
|
-
|
|
61
|
-
**❌ Incorrect (sequential execution, 3 round trips):**
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
const user = await fetchUser()
|
|
65
|
-
const posts = await fetchPosts()
|
|
66
|
-
const comments = await fetchComments()
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**✅ Correct (parallel execution, 1 round trip):**
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
const [user, posts, comments] = await Promise.all([
|
|
73
|
-
fetchUser(),
|
|
74
|
-
fetchPosts(),
|
|
75
|
-
fetchComments()
|
|
76
|
-
])
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### Strategic Suspense Boundaries
|
|
80
|
-
|
|
81
|
-
**Impact:** Faster initial paint
|
|
82
|
-
|
|
83
|
-
Instead of awaiting data before returning JSX, use Suspense to show wrapper UI faster.
|
|
84
|
-
|
|
85
|
-
**❌ Incorrect (wrapper blocked by data fetching):**
|
|
86
|
-
|
|
87
|
-
```tsx
|
|
88
|
-
async function Page() {
|
|
89
|
-
const data = await fetchData() // Blocks entire page
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<div>
|
|
93
|
-
<div>Sidebar</div>
|
|
94
|
-
<div>Header</div>
|
|
95
|
-
<div>
|
|
96
|
-
<DataDisplay data={data} />
|
|
97
|
-
</div>
|
|
98
|
-
<div>Footer</div>
|
|
99
|
-
</div>
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**✅ Correct (wrapper shows immediately, data streams in):**
|
|
105
|
-
|
|
106
|
-
```tsx
|
|
107
|
-
function Page() {
|
|
108
|
-
return (
|
|
109
|
-
<div>
|
|
110
|
-
<div>Sidebar</div>
|
|
111
|
-
<div>Header</div>
|
|
112
|
-
<div>
|
|
113
|
-
<Suspense fallback={<Skeleton />}>
|
|
114
|
-
<DataDisplay />
|
|
115
|
-
</Suspense>
|
|
116
|
-
</div>
|
|
117
|
-
<div>Footer</div>
|
|
118
|
-
</div>
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function DataDisplay() {
|
|
123
|
-
const data = await fetchData() // Only blocks this component
|
|
124
|
-
return <div>{data.content}</div>
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
**Alternative (share promise across components):**
|
|
129
|
-
|
|
130
|
-
```tsx
|
|
131
|
-
function Page() {
|
|
132
|
-
const dataPromise = fetchData() // Start fetch immediately, don't await
|
|
133
|
-
|
|
134
|
-
return (
|
|
135
|
-
<div>
|
|
136
|
-
<div>Sidebar</div>
|
|
137
|
-
<Suspense fallback={<Skeleton />}>
|
|
138
|
-
<DataDisplay dataPromise={dataPromise} />
|
|
139
|
-
<DataSummary dataPromise={dataPromise} />
|
|
140
|
-
</Suspense>
|
|
141
|
-
</div>
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
|
|
146
|
-
const data = use(dataPromise) // Unwraps the promise
|
|
147
|
-
return <div>{data.content}</div>
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## 2. Bundle Size Optimization (CRITICAL)
|
|
154
|
-
|
|
155
|
-
Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.
|
|
156
|
-
|
|
157
|
-
### Avoid Barrel File Imports
|
|
158
|
-
|
|
159
|
-
**Impact:** 200-800ms import cost, slow builds
|
|
160
|
-
|
|
161
|
-
Import directly from source files instead of barrel files.
|
|
162
|
-
|
|
163
|
-
**❌ Incorrect (imports entire library):**
|
|
164
|
-
|
|
165
|
-
```tsx
|
|
166
|
-
import { Check, X, Menu } from 'lucide-react'
|
|
167
|
-
// Loads 1,583 modules, takes ~2.8s extra in dev
|
|
168
|
-
|
|
169
|
-
import { Button, TextField } from '@mui/material'
|
|
170
|
-
// Loads 2,225 modules, takes ~4.2s extra in dev
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
**✅ Correct (imports only what you need):**
|
|
174
|
-
|
|
175
|
-
```tsx
|
|
176
|
-
import Check from 'lucide-react/dist/esm/icons/check'
|
|
177
|
-
import X from 'lucide-react/dist/esm/icons/x'
|
|
178
|
-
import Menu from 'lucide-react/dist/esm/icons/menu'
|
|
179
|
-
|
|
180
|
-
import Button from '@mui/material/Button'
|
|
181
|
-
import TextField from '@mui/material/TextField'
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
**Alternative (Next.js 13.5+):**
|
|
185
|
-
|
|
186
|
-
```js
|
|
187
|
-
// next.config.js
|
|
188
|
-
module.exports = {
|
|
189
|
-
experimental: {
|
|
190
|
-
optimizePackageImports: ['lucide-react', '@mui/material']
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
**Commonly affected libraries:** `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `date-fns`.
|
|
196
|
-
|
|
197
|
-
### Dynamic Imports for Heavy Components
|
|
198
|
-
|
|
199
|
-
**Impact:** Directly affects TTI and LCP
|
|
200
|
-
|
|
201
|
-
Use `next/dynamic` to lazy-load large components not needed on initial render.
|
|
202
|
-
|
|
203
|
-
**❌ Incorrect (Monaco bundles with main chunk ~300KB):**
|
|
204
|
-
|
|
205
|
-
```tsx
|
|
206
|
-
import { MonacoEditor } from './monaco-editor'
|
|
207
|
-
|
|
208
|
-
function CodePanel({ code }: { code: string }) {
|
|
209
|
-
return <MonacoEditor value={code} />
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
**✅ Correct (Monaco loads on demand):**
|
|
214
|
-
|
|
215
|
-
```tsx
|
|
216
|
-
import dynamic from 'next/dynamic'
|
|
217
|
-
|
|
218
|
-
const MonacoEditor = dynamic(
|
|
219
|
-
() => import('./monaco-editor').then(m => m.MonacoEditor),
|
|
220
|
-
{ ssr: false }
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
function CodePanel({ code }: { code: string }) {
|
|
224
|
-
return <MonacoEditor value={code} />
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
## 3. Server-Side Performance (HIGH)
|
|
231
|
-
|
|
232
|
-
Optimizing server-side rendering reduces response times.
|
|
233
|
-
|
|
234
|
-
### Per-Request Deduplication with React.cache()
|
|
235
|
-
|
|
236
|
-
**Impact:** Deduplicates within request
|
|
237
|
-
|
|
238
|
-
Use `React.cache()` for server-side request deduplication.
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
import { cache } from 'react'
|
|
31
|
+
Performance optimization guide for React and Next.js applications.
|
|
242
32
|
|
|
243
|
-
|
|
244
|
-
const session = await auth()
|
|
245
|
-
if (!session?.user?.id) return null
|
|
246
|
-
return await db.user.findUnique({
|
|
247
|
-
where: { id: session.user.id }
|
|
248
|
-
})
|
|
249
|
-
})
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
**Avoid inline objects as arguments** - use primitives for cache hits:
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
// ❌ Always cache miss
|
|
256
|
-
const getUser = cache(async (params: { uid: number }) => {...})
|
|
257
|
-
getUser({ uid: 1 })
|
|
258
|
-
getUser({ uid: 1 }) // Cache miss, runs query again
|
|
259
|
-
|
|
260
|
-
// ✅ Cache hit
|
|
261
|
-
const getUser = cache(async (uid: number) => {...})
|
|
262
|
-
getUser(1)
|
|
263
|
-
getUser(1) // Cache hit, returns cached result
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
**Note:** Next.js `fetch` has automatic memoization. Use `React.cache()` for:
|
|
267
|
-
- Database queries (Prisma, Drizzle, etc.)
|
|
268
|
-
- Heavy computations
|
|
269
|
-
- Authentication checks
|
|
270
|
-
- File system operations
|
|
271
|
-
|
|
272
|
-
### Parallel Data Fetching with Component Composition
|
|
273
|
-
|
|
274
|
-
**Impact:** Eliminates server-side waterfalls
|
|
275
|
-
|
|
276
|
-
React Server Components execute sequentially within a tree. Restructure with composition to parallelize.
|
|
277
|
-
|
|
278
|
-
**❌ Incorrect (Sidebar waits for Page's fetch):**
|
|
279
|
-
|
|
280
|
-
```tsx
|
|
281
|
-
export default async function Page() {
|
|
282
|
-
const header = await fetchHeader()
|
|
283
|
-
return (
|
|
284
|
-
<div>
|
|
285
|
-
<div>{header}</div>
|
|
286
|
-
<Sidebar />
|
|
287
|
-
</div>
|
|
288
|
-
)
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
async function Sidebar() {
|
|
292
|
-
const items = await fetchSidebarItems()
|
|
293
|
-
return <nav>{items.map(renderItem)}</nav>
|
|
294
|
-
}
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
**✅ Correct (both fetch simultaneously):**
|
|
298
|
-
|
|
299
|
-
```tsx
|
|
300
|
-
async function Header() {
|
|
301
|
-
const data = await fetchHeader()
|
|
302
|
-
return <div>{data}</div>
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
async function Sidebar() {
|
|
306
|
-
const items = await fetchSidebarItems()
|
|
307
|
-
return <nav>{items.map(renderItem)}</nav>
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export default function Page() {
|
|
311
|
-
return (
|
|
312
|
-
<div>
|
|
313
|
-
<Header />
|
|
314
|
-
<Sidebar />
|
|
315
|
-
</div>
|
|
316
|
-
)
|
|
317
|
-
}
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
---
|
|
321
|
-
|
|
322
|
-
## 4. Client-Side Data Fetching (MEDIUM-HIGH)
|
|
323
|
-
|
|
324
|
-
### Use SWR for Automatic Deduplication
|
|
325
|
-
|
|
326
|
-
**Impact:** Automatic deduplication and caching
|
|
327
|
-
|
|
328
|
-
**❌ Incorrect (no deduplication, each instance fetches):**
|
|
329
|
-
|
|
330
|
-
```tsx
|
|
331
|
-
function UserList() {
|
|
332
|
-
const [users, setUsers] = useState([])
|
|
333
|
-
useEffect(() => {
|
|
334
|
-
fetch('/api/users')
|
|
335
|
-
.then(r => r.json())
|
|
336
|
-
.then(setUsers)
|
|
337
|
-
}, [])
|
|
338
|
-
}
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
**✅ Correct (multiple instances share one request):**
|
|
342
|
-
|
|
343
|
-
```tsx
|
|
344
|
-
import useSWR from 'swr'
|
|
345
|
-
|
|
346
|
-
function UserList() {
|
|
347
|
-
const { data: users } = useSWR('/api/users', fetcher)
|
|
348
|
-
}
|
|
349
|
-
```
|
|
33
|
+
For detailed code examples, see `references/code-patterns.md`.
|
|
350
34
|
|
|
351
35
|
---
|
|
352
36
|
|
|
353
|
-
##
|
|
354
|
-
|
|
355
|
-
Reducing unnecessary re-renders minimizes wasted computation.
|
|
356
|
-
|
|
357
|
-
### Extract to Memoized Components
|
|
358
|
-
|
|
359
|
-
**Impact:** Enables early returns
|
|
360
|
-
|
|
361
|
-
**❌ Incorrect (computes avatar even when loading):**
|
|
362
|
-
|
|
363
|
-
```tsx
|
|
364
|
-
function Profile({ user, loading }: Props) {
|
|
365
|
-
const avatar = useMemo(() => {
|
|
366
|
-
const id = computeAvatarId(user)
|
|
367
|
-
return <Avatar id={id} />
|
|
368
|
-
}, [user])
|
|
369
|
-
|
|
370
|
-
if (loading) return <Skeleton />
|
|
371
|
-
return <div>{avatar}</div>
|
|
372
|
-
}
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
**✅ Correct (skips computation when loading):**
|
|
376
|
-
|
|
377
|
-
```tsx
|
|
378
|
-
const UserAvatar = memo(function UserAvatar({ user }: { user: User }) {
|
|
379
|
-
const id = useMemo(() => computeAvatarId(user), [user])
|
|
380
|
-
return <Avatar id={id} />
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
function Profile({ user, loading }: Props) {
|
|
384
|
-
if (loading) return <Skeleton />
|
|
385
|
-
return (
|
|
386
|
-
<div>
|
|
387
|
-
<UserAvatar user={user} />
|
|
388
|
-
</div>
|
|
389
|
-
)
|
|
390
|
-
}
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
**Note:** If React Compiler is enabled, manual memoization is not necessary.
|
|
394
|
-
|
|
395
|
-
### Subscribe to Derived State
|
|
396
|
-
|
|
397
|
-
**Impact:** Reduces re-render frequency
|
|
398
|
-
|
|
399
|
-
**❌ Incorrect (re-renders on every pixel change):**
|
|
400
|
-
|
|
401
|
-
```tsx
|
|
402
|
-
function Sidebar() {
|
|
403
|
-
const width = useWindowWidth() // updates continuously
|
|
404
|
-
const isMobile = width < 768
|
|
405
|
-
return <nav className={isMobile ? 'mobile' : 'desktop'} />
|
|
406
|
-
}
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
**✅ Correct (re-renders only when boolean changes):**
|
|
37
|
+
## Rule Categories by Priority
|
|
410
38
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
39
|
+
| Priority | Category | Impact |
|
|
40
|
+
|----------|----------|--------|
|
|
41
|
+
| 1 | Eliminating Waterfalls | CRITICAL |
|
|
42
|
+
| 2 | Bundle Size Optimization | CRITICAL |
|
|
43
|
+
| 3 | Server-Side Performance | HIGH |
|
|
44
|
+
| 4 | Client-Side Data Fetching | MEDIUM-HIGH |
|
|
45
|
+
| 5 | Re-render Optimization | MEDIUM |
|
|
46
|
+
| 6 | Rendering Performance | MEDIUM |
|
|
47
|
+
| 7 | JavaScript Performance | LOW-MEDIUM |
|
|
417
48
|
|
|
418
49
|
---
|
|
419
50
|
|
|
420
|
-
##
|
|
421
|
-
|
|
422
|
-
### Use Explicit Conditional Rendering
|
|
51
|
+
## Quick Rules Summary
|
|
423
52
|
|
|
424
|
-
|
|
53
|
+
### 1. Eliminating Waterfalls (CRITICAL)
|
|
425
54
|
|
|
426
|
-
|
|
55
|
+
- **Use `Promise.all()`** for independent async operations
|
|
56
|
+
- **Use Suspense boundaries** to show wrapper UI faster
|
|
57
|
+
- **Don't await** in parent when child can fetch independently
|
|
427
58
|
|
|
428
|
-
|
|
429
|
-
function Badge({ count }: { count: number }) {
|
|
430
|
-
return (
|
|
431
|
-
<div>
|
|
432
|
-
{count && <span className="badge">{count}</span>}
|
|
433
|
-
</div>
|
|
434
|
-
)
|
|
435
|
-
}
|
|
436
|
-
// When count = 0, renders: <div>0</div>
|
|
437
|
-
```
|
|
59
|
+
### 2. Bundle Size (CRITICAL)
|
|
438
60
|
|
|
439
|
-
|
|
61
|
+
- **Avoid barrel imports** - import directly from source files
|
|
62
|
+
- **Use `next/dynamic`** for heavy components (Monaco, charts, etc.)
|
|
63
|
+
- **Configure `optimizePackageImports`** in Next.js 13.5+
|
|
64
|
+
- **Affected libs**: lucide-react, @mui/material, lodash, date-fns
|
|
440
65
|
|
|
441
|
-
|
|
442
|
-
function Badge({ count }: { count: number }) {
|
|
443
|
-
return (
|
|
444
|
-
<div>
|
|
445
|
-
{count > 0 ? <span className="badge">{count}</span> : null}
|
|
446
|
-
</div>
|
|
447
|
-
)
|
|
448
|
-
}
|
|
449
|
-
```
|
|
66
|
+
### 3. Server-Side Performance (HIGH)
|
|
450
67
|
|
|
451
|
-
|
|
68
|
+
- **Use `React.cache()`** for server-side request deduplication
|
|
69
|
+
- **Use primitives** as cache arguments (not objects)
|
|
70
|
+
- **Restructure RSC** for parallel data fetching via composition
|
|
452
71
|
|
|
453
|
-
|
|
72
|
+
### 4. Client-Side Data Fetching (MEDIUM-HIGH)
|
|
454
73
|
|
|
455
|
-
|
|
74
|
+
- **Use SWR** for automatic deduplication and caching
|
|
75
|
+
- **Avoid useEffect + fetch** pattern
|
|
456
76
|
|
|
457
|
-
###
|
|
77
|
+
### 5. Re-render Optimization (MEDIUM)
|
|
458
78
|
|
|
459
|
-
**
|
|
79
|
+
- **Extract to memoized components** for early returns
|
|
80
|
+
- **Subscribe to derived booleans**, not raw values
|
|
81
|
+
- **Note**: React Compiler makes manual memo unnecessary
|
|
460
82
|
|
|
461
|
-
|
|
83
|
+
### 6. Rendering Performance (MEDIUM)
|
|
462
84
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
...order,
|
|
467
|
-
user: users.find(u => u.id === order.userId)
|
|
468
|
-
}))
|
|
469
|
-
}
|
|
470
|
-
```
|
|
85
|
+
- **Use explicit ternary** for number conditionals
|
|
86
|
+
- `count && <X/>` renders "0" when count is 0
|
|
87
|
+
- Use `count > 0 ? <X/> : null` instead
|
|
471
88
|
|
|
472
|
-
|
|
89
|
+
### 7. JavaScript Performance (LOW-MEDIUM)
|
|
473
90
|
|
|
474
|
-
|
|
475
|
-
function processOrders(orders: Order[], users: User[]) {
|
|
476
|
-
const userById = new Map(users.map(u => [u.id, u]))
|
|
477
|
-
|
|
478
|
-
return orders.map(order => ({
|
|
479
|
-
...order,
|
|
480
|
-
user: userById.get(order.userId)
|
|
481
|
-
}))
|
|
482
|
-
}
|
|
483
|
-
```
|
|
91
|
+
- **Build Map/Set** for repeated lookups (O(1) vs O(n))
|
|
484
92
|
|
|
485
93
|
---
|
|
486
94
|
|
|
@@ -517,3 +125,5 @@ function processOrders(orders: Order[], users: User[]) {
|
|
|
517
125
|
- [SWR documentation](https://swr.vercel.app)
|
|
518
126
|
- [Next.js Package Imports Optimization](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)
|
|
519
127
|
- [React Compiler](https://react.dev/learn/react-compiler)
|
|
128
|
+
|
|
129
|
+
For detailed code patterns and examples: `references/code-patterns.md`
|