cursor-kit-cli 1.2.0-beta → 1.2.0-beta.3
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/bin/cursor-reinstall-instance.sh +102 -0
- package/dist/cli.cjs +366 -69
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +367 -70
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +39 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +33 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/commands/docs.md +5 -3
- package/templates/commands/explain.md +5 -3
- package/templates/commands/fix.md +5 -3
- package/templates/commands/implement.md +5 -3
- package/templates/commands/refactor.md +5 -3
- package/templates/commands/review.md +5 -3
- package/templates/commands/test.md +5 -3
- package/templates/manifest.json +11 -8
- package/templates/rules/git.mdc +0 -2
- package/templates/rules/toc.mdc +17 -9
- package/templates/skills/aesthetic/SKILL.md +121 -0
- package/templates/skills/aesthetic/assets/design-guideline-template.md +163 -0
- package/templates/skills/aesthetic/assets/design-story-template.md +135 -0
- package/templates/skills/aesthetic/references/design-principles.md +62 -0
- package/templates/skills/aesthetic/references/design-resources.md +75 -0
- package/templates/skills/aesthetic/references/micro-interactions.md +53 -0
- package/templates/skills/aesthetic/references/storytelling-design.md +50 -0
- package/templates/skills/backend-development/SKILL.mdc +95 -0
- package/templates/skills/backend-development/references/backend-api-design.md +495 -0
- package/templates/skills/backend-development/references/backend-architecture.md +454 -0
- package/templates/skills/backend-development/references/backend-authentication.md +338 -0
- package/templates/skills/backend-development/references/backend-code-quality.md +659 -0
- package/templates/skills/backend-development/references/backend-debugging.md +904 -0
- package/templates/skills/backend-development/references/backend-devops.md +494 -0
- package/templates/skills/backend-development/references/backend-mindset.md +387 -0
- package/templates/skills/backend-development/references/backend-performance.md +397 -0
- package/templates/skills/backend-development/references/backend-security.md +290 -0
- package/templates/skills/backend-development/references/backend-technologies.md +256 -0
- package/templates/skills/backend-development/references/backend-testing.md +429 -0
- package/templates/skills/frontend-design/SKILL.mdc +41 -0
- package/templates/skills/frontend-design/references/animejs.md +396 -0
- package/templates/skills/frontend-development/SKILL.mdc +399 -0
- package/templates/skills/frontend-development/resources/common-patterns.md +331 -0
- package/templates/skills/frontend-development/resources/complete-examples.md +872 -0
- package/templates/skills/frontend-development/resources/component-patterns.md +502 -0
- package/templates/skills/frontend-development/resources/data-fetching.md +767 -0
- package/templates/skills/frontend-development/resources/file-organization.md +502 -0
- package/templates/skills/frontend-development/resources/loading-and-error-states.md +501 -0
- package/templates/skills/frontend-development/resources/performance.md +406 -0
- package/templates/skills/frontend-development/resources/routing-guide.md +364 -0
- package/templates/skills/frontend-development/resources/styling-guide.md +428 -0
- package/templates/skills/frontend-development/resources/typescript-standards.md +418 -0
- package/templates/skills/problem-solving/SKILL.mdc +96 -0
- package/templates/skills/problem-solving/references/attribution.md +69 -0
- package/templates/skills/problem-solving/references/collision-zone-thinking.md +79 -0
- package/templates/skills/problem-solving/references/inversion-exercise.md +91 -0
- package/templates/skills/problem-solving/references/meta-pattern-recognition.md +87 -0
- package/templates/skills/problem-solving/references/scale-game.md +95 -0
- package/templates/skills/problem-solving/references/simplification-cascades.md +80 -0
- package/templates/skills/problem-solving/references/when-stuck.md +72 -0
- package/templates/skills/research/SKILL.mdc +168 -0
- package/templates/skills/sequential-thinking/.env.example +8 -0
- package/templates/skills/sequential-thinking/README.md +183 -0
- package/templates/skills/sequential-thinking/SKILL.mdc +94 -0
- package/templates/skills/sequential-thinking/package.json +31 -0
- package/templates/skills/sequential-thinking/references/advanced-strategies.md +79 -0
- package/templates/skills/sequential-thinking/references/advanced-techniques.md +76 -0
- package/templates/skills/sequential-thinking/references/core-patterns.md +95 -0
- package/templates/skills/sequential-thinking/references/examples-api.md +88 -0
- package/templates/skills/sequential-thinking/references/examples-architecture.md +94 -0
- package/templates/skills/sequential-thinking/references/examples-debug.md +90 -0
- package/templates/skills/sequential-thinking/scripts/format-thought.js +159 -0
- package/templates/skills/sequential-thinking/scripts/process-thought.js +236 -0
- package/templates/skills/sequential-thinking/tests/format-thought.test.js +133 -0
- package/templates/skills/sequential-thinking/tests/process-thought.test.js +215 -0
- package/templates/skills/ui-styling/LICENSE.txt +202 -0
- package/templates/skills/ui-styling/SKILL.mdc +321 -0
- package/templates/skills/ui-styling/references/canvas-design-system.md +320 -0
- package/templates/skills/ui-styling/references/shadcn-accessibility.md +471 -0
- package/templates/skills/ui-styling/references/shadcn-components.md +424 -0
- package/templates/skills/ui-styling/references/shadcn-theming.md +373 -0
- package/templates/skills/ui-styling/references/tailwind-customization.md +483 -0
- package/templates/skills/ui-styling/references/tailwind-responsive.md +382 -0
- package/templates/skills/ui-styling/references/tailwind-utilities.md +455 -0
- package/templates/rules/frontend-design.mdc +0 -48
- package/templates/rules/performance.mdc +0 -54
- package/templates/rules/react.mdc +0 -58
- package/templates/rules/security.mdc +0 -50
- package/templates/rules/testing.mdc +0 -54
- package/templates/rules/typescript.mdc +0 -36
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
# Component Patterns
|
|
2
|
+
|
|
3
|
+
Modern React component architecture for the application emphasizing type safety, lazy loading, and Suspense boundaries.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## React.FC Pattern (PREFERRED)
|
|
8
|
+
|
|
9
|
+
### Why React.FC
|
|
10
|
+
|
|
11
|
+
All components use the `React.FC<Props>` pattern for:
|
|
12
|
+
- Explicit type safety for props
|
|
13
|
+
- Consistent component signatures
|
|
14
|
+
- Clear prop interface documentation
|
|
15
|
+
- Better IDE autocomplete
|
|
16
|
+
|
|
17
|
+
### Basic Pattern
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import React from 'react';
|
|
21
|
+
|
|
22
|
+
interface MyComponentProps {
|
|
23
|
+
/** User ID to display */
|
|
24
|
+
userId: number;
|
|
25
|
+
/** Optional callback when action occurs */
|
|
26
|
+
onAction?: () => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const MyComponent: React.FC<MyComponentProps> = ({ userId, onAction }) => {
|
|
30
|
+
return (
|
|
31
|
+
<div>
|
|
32
|
+
User: {userId}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default MyComponent;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Key Points:**
|
|
41
|
+
- Props interface defined separately with JSDoc comments
|
|
42
|
+
- `React.FC<Props>` provides type safety
|
|
43
|
+
- Destructure props in parameters
|
|
44
|
+
- Default export at bottom
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Lazy Loading Pattern
|
|
49
|
+
|
|
50
|
+
### When to Lazy Load
|
|
51
|
+
|
|
52
|
+
Lazy load components that are:
|
|
53
|
+
- Heavy (DataGrid, charts, rich text editors)
|
|
54
|
+
- Route-level components
|
|
55
|
+
- Modal/dialog content (not shown initially)
|
|
56
|
+
- Below-the-fold content
|
|
57
|
+
|
|
58
|
+
### How to Lazy Load
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import React from 'react';
|
|
62
|
+
|
|
63
|
+
// Lazy load heavy component
|
|
64
|
+
const PostDataGrid = React.lazy(() =>
|
|
65
|
+
import('./grids/PostDataGrid')
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// For named exports
|
|
69
|
+
const MyComponent = React.lazy(() =>
|
|
70
|
+
import('./MyComponent').then(module => ({
|
|
71
|
+
default: module.MyComponent
|
|
72
|
+
}))
|
|
73
|
+
);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Example from PostTable.tsx:**
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
/**
|
|
80
|
+
* Main post table container component
|
|
81
|
+
*/
|
|
82
|
+
import React, { useState, useCallback } from 'react';
|
|
83
|
+
import { Box, Paper } from '@mui/material';
|
|
84
|
+
|
|
85
|
+
// Lazy load PostDataGrid to optimize bundle size
|
|
86
|
+
const PostDataGrid = React.lazy(() => import('./grids/PostDataGrid'));
|
|
87
|
+
|
|
88
|
+
import { SuspenseLoader } from '~components/SuspenseLoader';
|
|
89
|
+
|
|
90
|
+
export const PostTable: React.FC<PostTableProps> = ({ formId }) => {
|
|
91
|
+
return (
|
|
92
|
+
<Box>
|
|
93
|
+
<SuspenseLoader>
|
|
94
|
+
<PostDataGrid formId={formId} />
|
|
95
|
+
</SuspenseLoader>
|
|
96
|
+
</Box>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default PostTable;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Suspense Boundaries
|
|
106
|
+
|
|
107
|
+
### SuspenseLoader Component
|
|
108
|
+
|
|
109
|
+
**Import:**
|
|
110
|
+
```typescript
|
|
111
|
+
import { SuspenseLoader } from '~components/SuspenseLoader';
|
|
112
|
+
// Or
|
|
113
|
+
import { SuspenseLoader } from '@/components/SuspenseLoader';
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Usage:**
|
|
117
|
+
```typescript
|
|
118
|
+
<SuspenseLoader>
|
|
119
|
+
<LazyLoadedComponent />
|
|
120
|
+
</SuspenseLoader>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**What it does:**
|
|
124
|
+
- Shows loading indicator while lazy component loads
|
|
125
|
+
- Smooth fade-in animation
|
|
126
|
+
- Consistent loading experience
|
|
127
|
+
- Prevents layout shift
|
|
128
|
+
|
|
129
|
+
### Where to Place Suspense Boundaries
|
|
130
|
+
|
|
131
|
+
**Route Level:**
|
|
132
|
+
```typescript
|
|
133
|
+
// routes/my-route/index.tsx
|
|
134
|
+
const MyPage = lazy(() => import('@/features/my-feature/components/MyPage'));
|
|
135
|
+
|
|
136
|
+
function Route() {
|
|
137
|
+
return (
|
|
138
|
+
<SuspenseLoader>
|
|
139
|
+
<MyPage />
|
|
140
|
+
</SuspenseLoader>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Component Level:**
|
|
146
|
+
```typescript
|
|
147
|
+
function ParentComponent() {
|
|
148
|
+
return (
|
|
149
|
+
<Box>
|
|
150
|
+
<Header />
|
|
151
|
+
<SuspenseLoader>
|
|
152
|
+
<HeavyDataGrid />
|
|
153
|
+
</SuspenseLoader>
|
|
154
|
+
</Box>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Multiple Boundaries:**
|
|
160
|
+
```typescript
|
|
161
|
+
function Page() {
|
|
162
|
+
return (
|
|
163
|
+
<Box>
|
|
164
|
+
<SuspenseLoader>
|
|
165
|
+
<HeaderSection />
|
|
166
|
+
</SuspenseLoader>
|
|
167
|
+
|
|
168
|
+
<SuspenseLoader>
|
|
169
|
+
<MainContent />
|
|
170
|
+
</SuspenseLoader>
|
|
171
|
+
|
|
172
|
+
<SuspenseLoader>
|
|
173
|
+
<Sidebar />
|
|
174
|
+
</SuspenseLoader>
|
|
175
|
+
</Box>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Each section loads independently, better UX.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Component Structure Template
|
|
185
|
+
|
|
186
|
+
### Recommended Order
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
/**
|
|
190
|
+
* Component description
|
|
191
|
+
* What it does, when to use it
|
|
192
|
+
*/
|
|
193
|
+
import React, { useState, useCallback, useMemo, useEffect } from 'react';
|
|
194
|
+
import { Box, Paper, Button } from '@mui/material';
|
|
195
|
+
import type { SxProps, Theme } from '@mui/material';
|
|
196
|
+
import { useSuspenseQuery } from '@tanstack/react-query';
|
|
197
|
+
|
|
198
|
+
// Feature imports
|
|
199
|
+
import { myFeatureApi } from '../api/myFeatureApi';
|
|
200
|
+
import type { MyData } from '~types/myData';
|
|
201
|
+
|
|
202
|
+
// Component imports
|
|
203
|
+
import { SuspenseLoader } from '~components/SuspenseLoader';
|
|
204
|
+
|
|
205
|
+
// Hooks
|
|
206
|
+
import { useAuth } from '@/hooks/useAuth';
|
|
207
|
+
import { useMuiSnackbar } from '@/hooks/useMuiSnackbar';
|
|
208
|
+
|
|
209
|
+
// 1. PROPS INTERFACE (with JSDoc)
|
|
210
|
+
interface MyComponentProps {
|
|
211
|
+
/** The ID of the entity to display */
|
|
212
|
+
entityId: number;
|
|
213
|
+
/** Optional callback when action completes */
|
|
214
|
+
onComplete?: () => void;
|
|
215
|
+
/** Display mode */
|
|
216
|
+
mode?: 'view' | 'edit';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 2. STYLES (if inline and <100 lines)
|
|
220
|
+
const componentStyles: Record<string, SxProps<Theme>> = {
|
|
221
|
+
container: {
|
|
222
|
+
p: 2,
|
|
223
|
+
display: 'flex',
|
|
224
|
+
flexDirection: 'column',
|
|
225
|
+
},
|
|
226
|
+
header: {
|
|
227
|
+
mb: 2,
|
|
228
|
+
display: 'flex',
|
|
229
|
+
justifyContent: 'space-between',
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// 3. COMPONENT DEFINITION
|
|
234
|
+
export const MyComponent: React.FC<MyComponentProps> = ({
|
|
235
|
+
entityId,
|
|
236
|
+
onComplete,
|
|
237
|
+
mode = 'view',
|
|
238
|
+
}) => {
|
|
239
|
+
// 4. HOOKS (in this order)
|
|
240
|
+
// - Context hooks first
|
|
241
|
+
const { user } = useAuth();
|
|
242
|
+
const { showSuccess, showError } = useMuiSnackbar();
|
|
243
|
+
|
|
244
|
+
// - Data fetching
|
|
245
|
+
const { data } = useSuspenseQuery({
|
|
246
|
+
queryKey: ['myEntity', entityId],
|
|
247
|
+
queryFn: () => myFeatureApi.getEntity(entityId),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// - Local state
|
|
251
|
+
const [selectedItem, setSelectedItem] = useState<string | null>(null);
|
|
252
|
+
const [isEditing, setIsEditing] = useState(mode === 'edit');
|
|
253
|
+
|
|
254
|
+
// - Memoized values
|
|
255
|
+
const filteredData = useMemo(() => {
|
|
256
|
+
return data.filter(item => item.active);
|
|
257
|
+
}, [data]);
|
|
258
|
+
|
|
259
|
+
// - Effects
|
|
260
|
+
useEffect(() => {
|
|
261
|
+
// Setup
|
|
262
|
+
return () => {
|
|
263
|
+
// Cleanup
|
|
264
|
+
};
|
|
265
|
+
}, []);
|
|
266
|
+
|
|
267
|
+
// 5. EVENT HANDLERS (with useCallback)
|
|
268
|
+
const handleItemSelect = useCallback((itemId: string) => {
|
|
269
|
+
setSelectedItem(itemId);
|
|
270
|
+
}, []);
|
|
271
|
+
|
|
272
|
+
const handleSave = useCallback(async () => {
|
|
273
|
+
try {
|
|
274
|
+
await myFeatureApi.updateEntity(entityId, { /* data */ });
|
|
275
|
+
showSuccess('Entity updated successfully');
|
|
276
|
+
onComplete?.();
|
|
277
|
+
} catch (error) {
|
|
278
|
+
showError('Failed to update entity');
|
|
279
|
+
}
|
|
280
|
+
}, [entityId, onComplete, showSuccess, showError]);
|
|
281
|
+
|
|
282
|
+
// 6. RENDER
|
|
283
|
+
return (
|
|
284
|
+
<Box sx={componentStyles.container}>
|
|
285
|
+
<Box sx={componentStyles.header}>
|
|
286
|
+
<h2>My Component</h2>
|
|
287
|
+
<Button onClick={handleSave}>Save</Button>
|
|
288
|
+
</Box>
|
|
289
|
+
|
|
290
|
+
<Paper sx={{ p: 2 }}>
|
|
291
|
+
{filteredData.map(item => (
|
|
292
|
+
<div key={item.id}>{item.name}</div>
|
|
293
|
+
))}
|
|
294
|
+
</Paper>
|
|
295
|
+
</Box>
|
|
296
|
+
);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// 7. EXPORT (default export at bottom)
|
|
300
|
+
export default MyComponent;
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Component Separation
|
|
306
|
+
|
|
307
|
+
### When to Split Components
|
|
308
|
+
|
|
309
|
+
**Split into multiple components when:**
|
|
310
|
+
- Component exceeds 300 lines
|
|
311
|
+
- Multiple distinct responsibilities
|
|
312
|
+
- Reusable sections
|
|
313
|
+
- Complex nested JSX
|
|
314
|
+
|
|
315
|
+
**Example:**
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
// ❌ AVOID - Monolithic
|
|
319
|
+
function MassiveComponent() {
|
|
320
|
+
// 500+ lines
|
|
321
|
+
// Search logic
|
|
322
|
+
// Filter logic
|
|
323
|
+
// Grid logic
|
|
324
|
+
// Action panel logic
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ✅ PREFERRED - Modular
|
|
328
|
+
function ParentContainer() {
|
|
329
|
+
return (
|
|
330
|
+
<Box>
|
|
331
|
+
<SearchAndFilter onFilter={handleFilter} />
|
|
332
|
+
<DataGrid data={filteredData} />
|
|
333
|
+
<ActionPanel onAction={handleAction} />
|
|
334
|
+
</Box>
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### When to Keep Together
|
|
340
|
+
|
|
341
|
+
**Keep in same file when:**
|
|
342
|
+
- Component < 200 lines
|
|
343
|
+
- Tightly coupled logic
|
|
344
|
+
- Not reusable elsewhere
|
|
345
|
+
- Simple presentation component
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Export Patterns
|
|
350
|
+
|
|
351
|
+
### Named Const + Default Export (PREFERRED)
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
export const MyComponent: React.FC<Props> = ({ ... }) => {
|
|
355
|
+
// Component logic
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
export default MyComponent;
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Why:**
|
|
362
|
+
- Named export for testing/refactoring
|
|
363
|
+
- Default export for lazy loading convenience
|
|
364
|
+
- Both options available to consumers
|
|
365
|
+
|
|
366
|
+
### Lazy Loading Named Exports
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
const MyComponent = React.lazy(() =>
|
|
370
|
+
import('./MyComponent').then(module => ({
|
|
371
|
+
default: module.MyComponent
|
|
372
|
+
}))
|
|
373
|
+
);
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Component Communication
|
|
379
|
+
|
|
380
|
+
### Props Down, Events Up
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
// Parent
|
|
384
|
+
function Parent() {
|
|
385
|
+
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
<Child
|
|
389
|
+
data={data} // Props down
|
|
390
|
+
onSelect={setSelectedId} // Events up
|
|
391
|
+
/>
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Child
|
|
396
|
+
interface ChildProps {
|
|
397
|
+
data: Data[];
|
|
398
|
+
onSelect: (id: string) => void;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export const Child: React.FC<ChildProps> = ({ data, onSelect }) => {
|
|
402
|
+
return (
|
|
403
|
+
<div onClick={() => onSelect(data[0].id)}>
|
|
404
|
+
{/* Content */}
|
|
405
|
+
</div>
|
|
406
|
+
);
|
|
407
|
+
};
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Avoid Prop Drilling
|
|
411
|
+
|
|
412
|
+
**Use context for deep nesting:**
|
|
413
|
+
```typescript
|
|
414
|
+
// ❌ AVOID - Prop drilling 5+ levels
|
|
415
|
+
<A prop={x}>
|
|
416
|
+
<B prop={x}>
|
|
417
|
+
<C prop={x}>
|
|
418
|
+
<D prop={x}>
|
|
419
|
+
<E prop={x} /> // Finally uses it here
|
|
420
|
+
</D>
|
|
421
|
+
</C>
|
|
422
|
+
</B>
|
|
423
|
+
</A>
|
|
424
|
+
|
|
425
|
+
// ✅ PREFERRED - Context or TanStack Query
|
|
426
|
+
const MyContext = createContext<MyData | null>(null);
|
|
427
|
+
|
|
428
|
+
function Provider({ children }) {
|
|
429
|
+
const { data } = useSuspenseQuery({ ... });
|
|
430
|
+
return <MyContext.Provider value={data}>{children}</MyContext.Provider>;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function DeepChild() {
|
|
434
|
+
const data = useContext(MyContext);
|
|
435
|
+
// Use data directly
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Advanced Patterns
|
|
442
|
+
|
|
443
|
+
### Compound Components
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
// Card.tsx
|
|
447
|
+
export const Card: React.FC<CardProps> & {
|
|
448
|
+
Header: typeof CardHeader;
|
|
449
|
+
Body: typeof CardBody;
|
|
450
|
+
Footer: typeof CardFooter;
|
|
451
|
+
} = ({ children }) => {
|
|
452
|
+
return <Paper>{children}</Paper>;
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
Card.Header = CardHeader;
|
|
456
|
+
Card.Body = CardBody;
|
|
457
|
+
Card.Footer = CardFooter;
|
|
458
|
+
|
|
459
|
+
// Usage
|
|
460
|
+
<Card>
|
|
461
|
+
<Card.Header>Title</Card.Header>
|
|
462
|
+
<Card.Body>Content</Card.Body>
|
|
463
|
+
<Card.Footer>Actions</Card.Footer>
|
|
464
|
+
</Card>
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Render Props (Rare, but useful)
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
interface DataProviderProps {
|
|
471
|
+
children: (data: Data) => React.ReactNode;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export const DataProvider: React.FC<DataProviderProps> = ({ children }) => {
|
|
475
|
+
const { data } = useSuspenseQuery({ ... });
|
|
476
|
+
return <>{children(data)}</>;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// Usage
|
|
480
|
+
<DataProvider>
|
|
481
|
+
{(data) => <Display data={data} />}
|
|
482
|
+
</DataProvider>
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Summary
|
|
488
|
+
|
|
489
|
+
**Modern Component Recipe:**
|
|
490
|
+
1. `React.FC<Props>` with TypeScript
|
|
491
|
+
2. Lazy load if heavy: `React.lazy(() => import())`
|
|
492
|
+
3. Wrap in `<SuspenseLoader>` for loading
|
|
493
|
+
4. Use `useSuspenseQuery` for data
|
|
494
|
+
5. Import aliases (@/, ~types, ~components)
|
|
495
|
+
6. Event handlers with `useCallback`
|
|
496
|
+
7. Default export at bottom
|
|
497
|
+
8. No early returns for loading states
|
|
498
|
+
|
|
499
|
+
**See Also:**
|
|
500
|
+
- [data-fetching.md](data-fetching.md) - useSuspenseQuery details
|
|
501
|
+
- [loading-and-error-states.md](loading-and-error-states.md) - Suspense best practices
|
|
502
|
+
- [complete-examples.md](complete-examples.md) - Full working examples
|