start-vibing 2.0.11 → 2.0.13
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/README.md +177 -177
- package/dist/cli.js +19 -2
- package/package.json +42 -42
- package/template/.claude/CLAUDE.md +174 -174
- package/template/.claude/agents/01-orchestration/agent-selector.md +130 -130
- package/template/.claude/agents/01-orchestration/checkpoint-manager.md +142 -142
- package/template/.claude/agents/01-orchestration/context-manager.md +138 -138
- package/template/.claude/agents/01-orchestration/error-recovery.md +182 -182
- package/template/.claude/agents/01-orchestration/orchestrator.md +114 -114
- package/template/.claude/agents/01-orchestration/parallel-coordinator.md +141 -141
- package/template/.claude/agents/01-orchestration/task-decomposer.md +121 -121
- package/template/.claude/agents/01-orchestration/workflow-router.md +114 -114
- package/template/.claude/agents/02-typescript/bun-runtime-expert.md +197 -197
- package/template/.claude/agents/02-typescript/esm-resolver.md +193 -193
- package/template/.claude/agents/02-typescript/import-alias-enforcer.md +158 -158
- package/template/.claude/agents/02-typescript/ts-generics-helper.md +183 -183
- package/template/.claude/agents/02-typescript/ts-migration-helper.md +238 -238
- package/template/.claude/agents/02-typescript/ts-strict-checker.md +180 -180
- package/template/.claude/agents/02-typescript/ts-types-analyzer.md +199 -199
- package/template/.claude/agents/02-typescript/type-definition-writer.md +187 -187
- package/template/.claude/agents/02-typescript/zod-schema-designer.md +212 -212
- package/template/.claude/agents/02-typescript/zod-validator.md +158 -158
- package/template/.claude/agents/03-testing/playwright-assertions.md +265 -265
- package/template/.claude/agents/03-testing/playwright-e2e.md +247 -247
- package/template/.claude/agents/03-testing/playwright-fixtures.md +234 -234
- package/template/.claude/agents/03-testing/playwright-multi-viewport.md +256 -256
- package/template/.claude/agents/03-testing/playwright-page-objects.md +247 -247
- package/template/.claude/agents/03-testing/test-cleanup-manager.md +248 -248
- package/template/.claude/agents/03-testing/test-data-generator.md +254 -254
- package/template/.claude/agents/03-testing/tester-integration.md +278 -278
- package/template/.claude/agents/03-testing/tester-unit.md +207 -207
- package/template/.claude/agents/03-testing/vitest-config.md +287 -287
- package/template/.claude/agents/04-docker/container-health.md +255 -255
- package/template/.claude/agents/04-docker/deployment-validator.md +225 -225
- package/template/.claude/agents/04-docker/docker-compose-designer.md +281 -281
- package/template/.claude/agents/04-docker/docker-env-manager.md +235 -235
- package/template/.claude/agents/04-docker/docker-multi-stage.md +241 -241
- package/template/.claude/agents/04-docker/dockerfile-optimizer.md +208 -208
- package/template/.claude/agents/05-database/database-seeder.md +273 -273
- package/template/.claude/agents/05-database/mongodb-query-optimizer.md +230 -230
- package/template/.claude/agents/05-database/mongoose-aggregation.md +306 -306
- package/template/.claude/agents/05-database/mongoose-index-optimizer.md +182 -182
- package/template/.claude/agents/05-database/mongoose-schema-designer.md +267 -267
- package/template/.claude/agents/06-security/auth-session-validator.md +68 -68
- package/template/.claude/agents/06-security/input-sanitizer.md +80 -80
- package/template/.claude/agents/06-security/owasp-checker.md +97 -97
- package/template/.claude/agents/06-security/permission-auditor.md +100 -100
- package/template/.claude/agents/06-security/security-auditor.md +84 -84
- package/template/.claude/agents/06-security/sensitive-data-scanner.md +83 -83
- package/template/.claude/agents/07-documentation/api-documenter.md +136 -136
- package/template/.claude/agents/07-documentation/changelog-manager.md +105 -105
- package/template/.claude/agents/07-documentation/documenter.md +76 -76
- package/template/.claude/agents/07-documentation/domain-updater.md +81 -81
- package/template/.claude/agents/07-documentation/jsdoc-generator.md +114 -114
- package/template/.claude/agents/07-documentation/readme-generator.md +135 -135
- package/template/.claude/agents/08-git/branch-manager.md +58 -58
- package/template/.claude/agents/08-git/commit-manager.md +63 -63
- package/template/.claude/agents/08-git/pr-creator.md +76 -76
- package/template/.claude/agents/09-quality/code-reviewer.md +71 -71
- package/template/.claude/agents/09-quality/quality-checker.md +67 -67
- package/template/.claude/agents/10-research/best-practices-finder.md +89 -89
- package/template/.claude/agents/10-research/competitor-analyzer.md +106 -106
- package/template/.claude/agents/10-research/pattern-researcher.md +93 -93
- package/template/.claude/agents/10-research/research-cache-manager.md +76 -76
- package/template/.claude/agents/10-research/research-web.md +98 -98
- package/template/.claude/agents/10-research/tech-evaluator.md +101 -101
- package/template/.claude/agents/11-ui-ux/accessibility-auditor.md +136 -136
- package/template/.claude/agents/11-ui-ux/design-system-enforcer.md +125 -125
- package/template/.claude/agents/11-ui-ux/skeleton-generator.md +118 -118
- package/template/.claude/agents/11-ui-ux/ui-desktop.md +132 -132
- package/template/.claude/agents/11-ui-ux/ui-mobile.md +98 -98
- package/template/.claude/agents/11-ui-ux/ui-tablet.md +110 -110
- package/template/.claude/agents/12-performance/api-latency-analyzer.md +156 -156
- package/template/.claude/agents/12-performance/bundle-analyzer.md +113 -113
- package/template/.claude/agents/12-performance/memory-leak-detector.md +137 -137
- package/template/.claude/agents/12-performance/performance-profiler.md +115 -115
- package/template/.claude/agents/12-performance/query-optimizer.md +124 -124
- package/template/.claude/agents/12-performance/render-optimizer.md +154 -154
- package/template/.claude/agents/13-debugging/build-error-fixer.md +207 -207
- package/template/.claude/agents/13-debugging/debugger.md +149 -149
- package/template/.claude/agents/13-debugging/error-stack-analyzer.md +141 -141
- package/template/.claude/agents/13-debugging/network-debugger.md +208 -208
- package/template/.claude/agents/13-debugging/runtime-error-fixer.md +181 -181
- package/template/.claude/agents/13-debugging/type-error-resolver.md +185 -185
- package/template/.claude/agents/14-validation/final-validator.md +93 -93
- package/template/.claude/agents/_backup/analyzer.md +134 -134
- package/template/.claude/agents/_backup/code-reviewer.md +279 -279
- package/template/.claude/agents/_backup/commit-manager.md +219 -219
- package/template/.claude/agents/_backup/debugger.md +280 -280
- package/template/.claude/agents/_backup/documenter.md +237 -237
- package/template/.claude/agents/_backup/domain-updater.md +197 -197
- package/template/.claude/agents/_backup/final-validator.md +169 -169
- package/template/.claude/agents/_backup/orchestrator.md +149 -149
- package/template/.claude/agents/_backup/performance.md +232 -232
- package/template/.claude/agents/_backup/quality-checker.md +240 -240
- package/template/.claude/agents/_backup/research.md +315 -315
- package/template/.claude/agents/_backup/security-auditor.md +192 -192
- package/template/.claude/agents/_backup/tester.md +566 -566
- package/template/.claude/agents/_backup/ui-ux-reviewer.md +247 -247
- package/template/.claude/config/README.md +30 -30
- package/template/.claude/config/mcp-config.json +344 -344
- package/template/.claude/config/project-config.json +53 -53
- package/template/.claude/config/quality-gates.json +46 -46
- package/template/.claude/config/security-rules.json +45 -45
- package/template/.claude/config/testing-config.json +164 -164
- package/template/.claude/hooks/SETUP.md +126 -126
- package/template/.claude/hooks/run-hook.ts +176 -176
- package/template/.claude/hooks/stop-validator.ts +914 -824
- package/template/.claude/hooks/user-prompt-submit.ts +886 -886
- package/template/.claude/scripts/mcp-quick-install.ts +151 -151
- package/template/.claude/scripts/setup-mcps.ts +651 -651
- package/template/.claude/settings.json +275 -275
- package/template/.claude/skills/bun-runtime/SKILL.md +430 -430
- package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +431 -431
- package/template/.claude/skills/codebase-knowledge/domains/mcp-integration.md +295 -295
- package/template/.claude/skills/debugging-patterns/SKILL.md +485 -485
- package/template/.claude/skills/docker-patterns/SKILL.md +555 -555
- package/template/.claude/skills/git-workflow/SKILL.md +454 -454
- package/template/.claude/skills/mongoose-patterns/SKILL.md +499 -499
- package/template/.claude/skills/nextjs-app-router/SKILL.md +327 -327
- package/template/.claude/skills/performance-patterns/SKILL.md +547 -547
- package/template/.claude/skills/playwright-automation/SKILL.md +438 -438
- package/template/.claude/skills/react-patterns/SKILL.md +389 -389
- package/template/.claude/skills/research-cache/SKILL.md +222 -222
- package/template/.claude/skills/shadcn-ui/SKILL.md +511 -511
- package/template/.claude/skills/tailwind-patterns/SKILL.md +465 -465
- package/template/.claude/skills/test-coverage/SKILL.md +467 -467
- package/template/.claude/skills/trpc-api/SKILL.md +434 -434
- package/template/.claude/skills/typescript-strict/SKILL.md +367 -367
- package/template/.claude/skills/zod-validation/SKILL.md +403 -403
- package/template/CLAUDE.md +117 -117
|
@@ -1,389 +1,389 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: react-patterns
|
|
3
|
-
description: Modern React patterns for React 19. Hooks, state management, component composition, performance optimization. Use when building React components.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Grep, Glob
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# React Patterns - Modern Component Architecture
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
Expert guidance for React 19 patterns:
|
|
12
|
-
|
|
13
|
-
- **Component Design** - Composition over inheritance
|
|
14
|
-
- **State Management** - Local, context, and server state
|
|
15
|
-
- **Performance** - Memoization and optimization
|
|
16
|
-
- **Hooks** - Built-in and custom hooks
|
|
17
|
-
- **TypeScript** - Proper typing patterns
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Component Patterns
|
|
22
|
-
|
|
23
|
-
### 1. Compound Components
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
// Composable API for complex UIs
|
|
27
|
-
interface TabsContextValue {
|
|
28
|
-
activeTab: string;
|
|
29
|
-
setActiveTab: (id: string) => void;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const TabsContext = createContext<TabsContextValue | null>(null);
|
|
33
|
-
|
|
34
|
-
function Tabs({ children, defaultTab }: { children: ReactNode; defaultTab: string }) {
|
|
35
|
-
const [activeTab, setActiveTab] = useState(defaultTab);
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
|
|
39
|
-
<div className="tabs">{children}</div>
|
|
40
|
-
</TabsContext.Provider>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function TabList({ children }: { children: ReactNode }) {
|
|
45
|
-
return (
|
|
46
|
-
<div className="tab-list" role="tablist">
|
|
47
|
-
{children}
|
|
48
|
-
</div>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function Tab({ id, children }: { id: string; children: ReactNode }) {
|
|
53
|
-
const context = useContext(TabsContext);
|
|
54
|
-
if (!context) throw new Error('Tab must be used within Tabs');
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<button
|
|
58
|
-
role="tab"
|
|
59
|
-
aria-selected={context.activeTab === id}
|
|
60
|
-
onClick={() => context.setActiveTab(id)}
|
|
61
|
-
>
|
|
62
|
-
{children}
|
|
63
|
-
</button>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function TabPanel({ id, children }: { id: string; children: ReactNode }) {
|
|
68
|
-
const context = useContext(TabsContext);
|
|
69
|
-
if (!context) throw new Error('TabPanel must be used within Tabs');
|
|
70
|
-
|
|
71
|
-
if (context.activeTab !== id) return null;
|
|
72
|
-
return <div role="tabpanel">{children}</div>;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Usage
|
|
76
|
-
<Tabs defaultTab="home">
|
|
77
|
-
<TabList>
|
|
78
|
-
<Tab id="home">Home</Tab>
|
|
79
|
-
<Tab id="settings">Settings</Tab>
|
|
80
|
-
</TabList>
|
|
81
|
-
<TabPanel id="home">Home content</TabPanel>
|
|
82
|
-
<TabPanel id="settings">Settings content</TabPanel>
|
|
83
|
-
</Tabs>;
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 2. Render Props Pattern
|
|
87
|
-
|
|
88
|
-
```tsx
|
|
89
|
-
interface MousePosition {
|
|
90
|
-
x: number;
|
|
91
|
-
y: number;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function MouseTracker({ render }: { render: (pos: MousePosition) => ReactNode }) {
|
|
95
|
-
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
96
|
-
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
const handleMove = (e: MouseEvent) => {
|
|
99
|
-
setPosition({ x: e.clientX, y: e.clientY });
|
|
100
|
-
};
|
|
101
|
-
window.addEventListener('mousemove', handleMove);
|
|
102
|
-
return () => window.removeEventListener('mousemove', handleMove);
|
|
103
|
-
}, []);
|
|
104
|
-
|
|
105
|
-
return <>{render(position)}</>;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Usage
|
|
109
|
-
<MouseTracker
|
|
110
|
-
render={({ x, y }) => (
|
|
111
|
-
<div>
|
|
112
|
-
Mouse: {x}, {y}
|
|
113
|
-
</div>
|
|
114
|
-
)}
|
|
115
|
-
/>;
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### 3. HOC (Higher-Order Component)
|
|
119
|
-
|
|
120
|
-
```tsx
|
|
121
|
-
function withAuth<P extends object>(Component: ComponentType<P>) {
|
|
122
|
-
return function AuthenticatedComponent(props: P) {
|
|
123
|
-
const { user, isLoading } = useAuth();
|
|
124
|
-
|
|
125
|
-
if (isLoading) return <Loading />;
|
|
126
|
-
if (!user) return <Redirect to="/login" />;
|
|
127
|
-
|
|
128
|
-
return <Component {...props} />;
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Usage
|
|
133
|
-
const ProtectedDashboard = withAuth(Dashboard);
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## Hooks Patterns
|
|
139
|
-
|
|
140
|
-
### Custom Hooks
|
|
141
|
-
|
|
142
|
-
```tsx
|
|
143
|
-
// useLocalStorage - Persist state
|
|
144
|
-
function useLocalStorage<T>(key: string, initialValue: T) {
|
|
145
|
-
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
146
|
-
if (typeof window === 'undefined') return initialValue;
|
|
147
|
-
try {
|
|
148
|
-
const item = window.localStorage.getItem(key);
|
|
149
|
-
return item ? JSON.parse(item) : initialValue;
|
|
150
|
-
} catch {
|
|
151
|
-
return initialValue;
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
const setValue = useCallback(
|
|
156
|
-
(value: T | ((val: T) => T)) => {
|
|
157
|
-
setStoredValue((prev) => {
|
|
158
|
-
const valueToStore = value instanceof Function ? value(prev) : value;
|
|
159
|
-
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
160
|
-
return valueToStore;
|
|
161
|
-
});
|
|
162
|
-
},
|
|
163
|
-
[key]
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
return [storedValue, setValue] as const;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// useDebounce - Debounced value
|
|
170
|
-
function useDebounce<T>(value: T, delay: number): T {
|
|
171
|
-
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
172
|
-
|
|
173
|
-
useEffect(() => {
|
|
174
|
-
const handler = setTimeout(() => setDebouncedValue(value), delay);
|
|
175
|
-
return () => clearTimeout(handler);
|
|
176
|
-
}, [value, delay]);
|
|
177
|
-
|
|
178
|
-
return debouncedValue;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// useAsync - Async operation state
|
|
182
|
-
function useAsync<T>(asyncFunction: () => Promise<T>, deps: unknown[] = []) {
|
|
183
|
-
const [state, setState] = useState<{
|
|
184
|
-
data: T | null;
|
|
185
|
-
loading: boolean;
|
|
186
|
-
error: Error | null;
|
|
187
|
-
}>({
|
|
188
|
-
data: null,
|
|
189
|
-
loading: true,
|
|
190
|
-
error: null,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
let mounted = true;
|
|
195
|
-
setState((s) => ({ ...s, loading: true }));
|
|
196
|
-
|
|
197
|
-
asyncFunction()
|
|
198
|
-
.then((data) => mounted && setState({ data, loading: false, error: null }))
|
|
199
|
-
.catch((error) => mounted && setState({ data: null, loading: false, error }));
|
|
200
|
-
|
|
201
|
-
return () => {
|
|
202
|
-
mounted = false;
|
|
203
|
-
};
|
|
204
|
-
}, deps);
|
|
205
|
-
|
|
206
|
-
return state;
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
|
-
## State Management
|
|
213
|
-
|
|
214
|
-
### 1. useReducer for Complex State
|
|
215
|
-
|
|
216
|
-
```tsx
|
|
217
|
-
type State = {
|
|
218
|
-
items: Item[];
|
|
219
|
-
loading: boolean;
|
|
220
|
-
error: string | null;
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
type Action =
|
|
224
|
-
| { type: 'FETCH_START' }
|
|
225
|
-
| { type: 'FETCH_SUCCESS'; payload: Item[] }
|
|
226
|
-
| { type: 'FETCH_ERROR'; payload: string }
|
|
227
|
-
| { type: 'ADD_ITEM'; payload: Item }
|
|
228
|
-
| { type: 'REMOVE_ITEM'; payload: string };
|
|
229
|
-
|
|
230
|
-
function reducer(state: State, action: Action): State {
|
|
231
|
-
switch (action.type) {
|
|
232
|
-
case 'FETCH_START':
|
|
233
|
-
return { ...state, loading: true, error: null };
|
|
234
|
-
case 'FETCH_SUCCESS':
|
|
235
|
-
return { ...state, loading: false, items: action.payload };
|
|
236
|
-
case 'FETCH_ERROR':
|
|
237
|
-
return { ...state, loading: false, error: action.payload };
|
|
238
|
-
case 'ADD_ITEM':
|
|
239
|
-
return { ...state, items: [...state.items, action.payload] };
|
|
240
|
-
case 'REMOVE_ITEM':
|
|
241
|
-
return { ...state, items: state.items.filter((i) => i.id !== action.payload) };
|
|
242
|
-
default:
|
|
243
|
-
return state;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### 2. Context for Global State
|
|
249
|
-
|
|
250
|
-
```tsx
|
|
251
|
-
interface AppContextValue {
|
|
252
|
-
user: User | null;
|
|
253
|
-
theme: 'light' | 'dark';
|
|
254
|
-
setTheme: (theme: 'light' | 'dark') => void;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const AppContext = createContext<AppContextValue | undefined>(undefined);
|
|
258
|
-
|
|
259
|
-
export function AppProvider({ children }: { children: ReactNode }) {
|
|
260
|
-
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
|
261
|
-
const { data: user } = useUser();
|
|
262
|
-
|
|
263
|
-
const value = useMemo(() => ({ user, theme, setTheme }), [user, theme]);
|
|
264
|
-
|
|
265
|
-
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export function useApp() {
|
|
269
|
-
const context = useContext(AppContext);
|
|
270
|
-
if (!context) throw new Error('useApp must be used within AppProvider');
|
|
271
|
-
return context;
|
|
272
|
-
}
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
---
|
|
276
|
-
|
|
277
|
-
## Performance Optimization
|
|
278
|
-
|
|
279
|
-
### 1. Memoization
|
|
280
|
-
|
|
281
|
-
```tsx
|
|
282
|
-
// memo - Prevent re-renders
|
|
283
|
-
const ExpensiveList = memo(function ExpensiveList({ items }: { items: Item[] }) {
|
|
284
|
-
return (
|
|
285
|
-
<ul>
|
|
286
|
-
{items.map((item) => (
|
|
287
|
-
<li key={item.id}>{item.name}</li>
|
|
288
|
-
))}
|
|
289
|
-
</ul>
|
|
290
|
-
);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// useMemo - Expensive calculations
|
|
294
|
-
function Dashboard({ data }: { data: DataPoint[] }) {
|
|
295
|
-
const processedData = useMemo(() => {
|
|
296
|
-
return data.map((d) => ({ ...d, computed: expensiveCalculation(d) }));
|
|
297
|
-
}, [data]);
|
|
298
|
-
|
|
299
|
-
return <Chart data={processedData} />;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// useCallback - Stable function references
|
|
303
|
-
function Parent() {
|
|
304
|
-
const [count, setCount] = useState(0);
|
|
305
|
-
|
|
306
|
-
const handleClick = useCallback(() => {
|
|
307
|
-
setCount((c) => c + 1);
|
|
308
|
-
}, []);
|
|
309
|
-
|
|
310
|
-
return <Child onClick={handleClick} />;
|
|
311
|
-
}
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### 2. Code Splitting
|
|
315
|
-
|
|
316
|
-
```tsx
|
|
317
|
-
import { lazy, Suspense } from 'react';
|
|
318
|
-
|
|
319
|
-
const HeavyComponent = lazy(() => import('./HeavyComponent'));
|
|
320
|
-
|
|
321
|
-
function App() {
|
|
322
|
-
return (
|
|
323
|
-
<Suspense fallback={<Loading />}>
|
|
324
|
-
<HeavyComponent />
|
|
325
|
-
</Suspense>
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
---
|
|
331
|
-
|
|
332
|
-
## TypeScript Patterns
|
|
333
|
-
|
|
334
|
-
### Props Types
|
|
335
|
-
|
|
336
|
-
```tsx
|
|
337
|
-
// Discriminated unions for variants
|
|
338
|
-
type ButtonProps = { variant: 'primary'; onClick: () => void } | { variant: 'link'; href: string };
|
|
339
|
-
|
|
340
|
-
function Button(props: ButtonProps) {
|
|
341
|
-
if (props.variant === 'link') {
|
|
342
|
-
return <a href={props.href}>Link</a>;
|
|
343
|
-
}
|
|
344
|
-
return <button onClick={props.onClick}>Button</button>;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Generic components
|
|
348
|
-
interface ListProps<T> {
|
|
349
|
-
items: T[];
|
|
350
|
-
renderItem: (item: T) => ReactNode;
|
|
351
|
-
keyExtractor: (item: T) => string;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
|
|
355
|
-
return (
|
|
356
|
-
<ul>
|
|
357
|
-
{items.map((item) => (
|
|
358
|
-
<li key={keyExtractor(item)}>{renderItem(item)}</li>
|
|
359
|
-
))}
|
|
360
|
-
</ul>
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
## Agent Integration
|
|
368
|
-
|
|
369
|
-
This skill is used by:
|
|
370
|
-
|
|
371
|
-
- **react-expert** subagent
|
|
372
|
-
- **render-optimizer** for performance issues
|
|
373
|
-
- **ui-mobile/tablet/desktop** for component patterns
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
|
-
## FORBIDDEN
|
|
378
|
-
|
|
379
|
-
1. **Class components** - Use function components
|
|
380
|
-
2. **Prop drilling** - Use context or composition
|
|
381
|
-
3. **Inline objects/functions in JSX** - Causes re-renders
|
|
382
|
-
4. **useEffect for derived state** - Use useMemo
|
|
383
|
-
5. **Mutating state directly** - Always use setState/dispatch
|
|
384
|
-
|
|
385
|
-
---
|
|
386
|
-
|
|
387
|
-
## Version
|
|
388
|
-
|
|
389
|
-
- **v1.0.0** - Initial implementation based on React 19 patterns
|
|
1
|
+
---
|
|
2
|
+
name: react-patterns
|
|
3
|
+
description: Modern React patterns for React 19. Hooks, state management, component composition, performance optimization. Use when building React components.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Grep, Glob
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Patterns - Modern Component Architecture
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Expert guidance for React 19 patterns:
|
|
12
|
+
|
|
13
|
+
- **Component Design** - Composition over inheritance
|
|
14
|
+
- **State Management** - Local, context, and server state
|
|
15
|
+
- **Performance** - Memoization and optimization
|
|
16
|
+
- **Hooks** - Built-in and custom hooks
|
|
17
|
+
- **TypeScript** - Proper typing patterns
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Component Patterns
|
|
22
|
+
|
|
23
|
+
### 1. Compound Components
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
// Composable API for complex UIs
|
|
27
|
+
interface TabsContextValue {
|
|
28
|
+
activeTab: string;
|
|
29
|
+
setActiveTab: (id: string) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const TabsContext = createContext<TabsContextValue | null>(null);
|
|
33
|
+
|
|
34
|
+
function Tabs({ children, defaultTab }: { children: ReactNode; defaultTab: string }) {
|
|
35
|
+
const [activeTab, setActiveTab] = useState(defaultTab);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
|
|
39
|
+
<div className="tabs">{children}</div>
|
|
40
|
+
</TabsContext.Provider>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function TabList({ children }: { children: ReactNode }) {
|
|
45
|
+
return (
|
|
46
|
+
<div className="tab-list" role="tablist">
|
|
47
|
+
{children}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function Tab({ id, children }: { id: string; children: ReactNode }) {
|
|
53
|
+
const context = useContext(TabsContext);
|
|
54
|
+
if (!context) throw new Error('Tab must be used within Tabs');
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<button
|
|
58
|
+
role="tab"
|
|
59
|
+
aria-selected={context.activeTab === id}
|
|
60
|
+
onClick={() => context.setActiveTab(id)}
|
|
61
|
+
>
|
|
62
|
+
{children}
|
|
63
|
+
</button>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function TabPanel({ id, children }: { id: string; children: ReactNode }) {
|
|
68
|
+
const context = useContext(TabsContext);
|
|
69
|
+
if (!context) throw new Error('TabPanel must be used within Tabs');
|
|
70
|
+
|
|
71
|
+
if (context.activeTab !== id) return null;
|
|
72
|
+
return <div role="tabpanel">{children}</div>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Usage
|
|
76
|
+
<Tabs defaultTab="home">
|
|
77
|
+
<TabList>
|
|
78
|
+
<Tab id="home">Home</Tab>
|
|
79
|
+
<Tab id="settings">Settings</Tab>
|
|
80
|
+
</TabList>
|
|
81
|
+
<TabPanel id="home">Home content</TabPanel>
|
|
82
|
+
<TabPanel id="settings">Settings content</TabPanel>
|
|
83
|
+
</Tabs>;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 2. Render Props Pattern
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
interface MousePosition {
|
|
90
|
+
x: number;
|
|
91
|
+
y: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function MouseTracker({ render }: { render: (pos: MousePosition) => ReactNode }) {
|
|
95
|
+
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const handleMove = (e: MouseEvent) => {
|
|
99
|
+
setPosition({ x: e.clientX, y: e.clientY });
|
|
100
|
+
};
|
|
101
|
+
window.addEventListener('mousemove', handleMove);
|
|
102
|
+
return () => window.removeEventListener('mousemove', handleMove);
|
|
103
|
+
}, []);
|
|
104
|
+
|
|
105
|
+
return <>{render(position)}</>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Usage
|
|
109
|
+
<MouseTracker
|
|
110
|
+
render={({ x, y }) => (
|
|
111
|
+
<div>
|
|
112
|
+
Mouse: {x}, {y}
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
/>;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 3. HOC (Higher-Order Component)
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
function withAuth<P extends object>(Component: ComponentType<P>) {
|
|
122
|
+
return function AuthenticatedComponent(props: P) {
|
|
123
|
+
const { user, isLoading } = useAuth();
|
|
124
|
+
|
|
125
|
+
if (isLoading) return <Loading />;
|
|
126
|
+
if (!user) return <Redirect to="/login" />;
|
|
127
|
+
|
|
128
|
+
return <Component {...props} />;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Usage
|
|
133
|
+
const ProtectedDashboard = withAuth(Dashboard);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Hooks Patterns
|
|
139
|
+
|
|
140
|
+
### Custom Hooks
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
// useLocalStorage - Persist state
|
|
144
|
+
function useLocalStorage<T>(key: string, initialValue: T) {
|
|
145
|
+
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
146
|
+
if (typeof window === 'undefined') return initialValue;
|
|
147
|
+
try {
|
|
148
|
+
const item = window.localStorage.getItem(key);
|
|
149
|
+
return item ? JSON.parse(item) : initialValue;
|
|
150
|
+
} catch {
|
|
151
|
+
return initialValue;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const setValue = useCallback(
|
|
156
|
+
(value: T | ((val: T) => T)) => {
|
|
157
|
+
setStoredValue((prev) => {
|
|
158
|
+
const valueToStore = value instanceof Function ? value(prev) : value;
|
|
159
|
+
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
160
|
+
return valueToStore;
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
[key]
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return [storedValue, setValue] as const;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// useDebounce - Debounced value
|
|
170
|
+
function useDebounce<T>(value: T, delay: number): T {
|
|
171
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
172
|
+
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
const handler = setTimeout(() => setDebouncedValue(value), delay);
|
|
175
|
+
return () => clearTimeout(handler);
|
|
176
|
+
}, [value, delay]);
|
|
177
|
+
|
|
178
|
+
return debouncedValue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// useAsync - Async operation state
|
|
182
|
+
function useAsync<T>(asyncFunction: () => Promise<T>, deps: unknown[] = []) {
|
|
183
|
+
const [state, setState] = useState<{
|
|
184
|
+
data: T | null;
|
|
185
|
+
loading: boolean;
|
|
186
|
+
error: Error | null;
|
|
187
|
+
}>({
|
|
188
|
+
data: null,
|
|
189
|
+
loading: true,
|
|
190
|
+
error: null,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
let mounted = true;
|
|
195
|
+
setState((s) => ({ ...s, loading: true }));
|
|
196
|
+
|
|
197
|
+
asyncFunction()
|
|
198
|
+
.then((data) => mounted && setState({ data, loading: false, error: null }))
|
|
199
|
+
.catch((error) => mounted && setState({ data: null, loading: false, error }));
|
|
200
|
+
|
|
201
|
+
return () => {
|
|
202
|
+
mounted = false;
|
|
203
|
+
};
|
|
204
|
+
}, deps);
|
|
205
|
+
|
|
206
|
+
return state;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## State Management
|
|
213
|
+
|
|
214
|
+
### 1. useReducer for Complex State
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
type State = {
|
|
218
|
+
items: Item[];
|
|
219
|
+
loading: boolean;
|
|
220
|
+
error: string | null;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
type Action =
|
|
224
|
+
| { type: 'FETCH_START' }
|
|
225
|
+
| { type: 'FETCH_SUCCESS'; payload: Item[] }
|
|
226
|
+
| { type: 'FETCH_ERROR'; payload: string }
|
|
227
|
+
| { type: 'ADD_ITEM'; payload: Item }
|
|
228
|
+
| { type: 'REMOVE_ITEM'; payload: string };
|
|
229
|
+
|
|
230
|
+
function reducer(state: State, action: Action): State {
|
|
231
|
+
switch (action.type) {
|
|
232
|
+
case 'FETCH_START':
|
|
233
|
+
return { ...state, loading: true, error: null };
|
|
234
|
+
case 'FETCH_SUCCESS':
|
|
235
|
+
return { ...state, loading: false, items: action.payload };
|
|
236
|
+
case 'FETCH_ERROR':
|
|
237
|
+
return { ...state, loading: false, error: action.payload };
|
|
238
|
+
case 'ADD_ITEM':
|
|
239
|
+
return { ...state, items: [...state.items, action.payload] };
|
|
240
|
+
case 'REMOVE_ITEM':
|
|
241
|
+
return { ...state, items: state.items.filter((i) => i.id !== action.payload) };
|
|
242
|
+
default:
|
|
243
|
+
return state;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 2. Context for Global State
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
interface AppContextValue {
|
|
252
|
+
user: User | null;
|
|
253
|
+
theme: 'light' | 'dark';
|
|
254
|
+
setTheme: (theme: 'light' | 'dark') => void;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const AppContext = createContext<AppContextValue | undefined>(undefined);
|
|
258
|
+
|
|
259
|
+
export function AppProvider({ children }: { children: ReactNode }) {
|
|
260
|
+
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
|
261
|
+
const { data: user } = useUser();
|
|
262
|
+
|
|
263
|
+
const value = useMemo(() => ({ user, theme, setTheme }), [user, theme]);
|
|
264
|
+
|
|
265
|
+
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function useApp() {
|
|
269
|
+
const context = useContext(AppContext);
|
|
270
|
+
if (!context) throw new Error('useApp must be used within AppProvider');
|
|
271
|
+
return context;
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Performance Optimization
|
|
278
|
+
|
|
279
|
+
### 1. Memoization
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
// memo - Prevent re-renders
|
|
283
|
+
const ExpensiveList = memo(function ExpensiveList({ items }: { items: Item[] }) {
|
|
284
|
+
return (
|
|
285
|
+
<ul>
|
|
286
|
+
{items.map((item) => (
|
|
287
|
+
<li key={item.id}>{item.name}</li>
|
|
288
|
+
))}
|
|
289
|
+
</ul>
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// useMemo - Expensive calculations
|
|
294
|
+
function Dashboard({ data }: { data: DataPoint[] }) {
|
|
295
|
+
const processedData = useMemo(() => {
|
|
296
|
+
return data.map((d) => ({ ...d, computed: expensiveCalculation(d) }));
|
|
297
|
+
}, [data]);
|
|
298
|
+
|
|
299
|
+
return <Chart data={processedData} />;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// useCallback - Stable function references
|
|
303
|
+
function Parent() {
|
|
304
|
+
const [count, setCount] = useState(0);
|
|
305
|
+
|
|
306
|
+
const handleClick = useCallback(() => {
|
|
307
|
+
setCount((c) => c + 1);
|
|
308
|
+
}, []);
|
|
309
|
+
|
|
310
|
+
return <Child onClick={handleClick} />;
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 2. Code Splitting
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import { lazy, Suspense } from 'react';
|
|
318
|
+
|
|
319
|
+
const HeavyComponent = lazy(() => import('./HeavyComponent'));
|
|
320
|
+
|
|
321
|
+
function App() {
|
|
322
|
+
return (
|
|
323
|
+
<Suspense fallback={<Loading />}>
|
|
324
|
+
<HeavyComponent />
|
|
325
|
+
</Suspense>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## TypeScript Patterns
|
|
333
|
+
|
|
334
|
+
### Props Types
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
// Discriminated unions for variants
|
|
338
|
+
type ButtonProps = { variant: 'primary'; onClick: () => void } | { variant: 'link'; href: string };
|
|
339
|
+
|
|
340
|
+
function Button(props: ButtonProps) {
|
|
341
|
+
if (props.variant === 'link') {
|
|
342
|
+
return <a href={props.href}>Link</a>;
|
|
343
|
+
}
|
|
344
|
+
return <button onClick={props.onClick}>Button</button>;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Generic components
|
|
348
|
+
interface ListProps<T> {
|
|
349
|
+
items: T[];
|
|
350
|
+
renderItem: (item: T) => ReactNode;
|
|
351
|
+
keyExtractor: (item: T) => string;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
|
|
355
|
+
return (
|
|
356
|
+
<ul>
|
|
357
|
+
{items.map((item) => (
|
|
358
|
+
<li key={keyExtractor(item)}>{renderItem(item)}</li>
|
|
359
|
+
))}
|
|
360
|
+
</ul>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Agent Integration
|
|
368
|
+
|
|
369
|
+
This skill is used by:
|
|
370
|
+
|
|
371
|
+
- **react-expert** subagent
|
|
372
|
+
- **render-optimizer** for performance issues
|
|
373
|
+
- **ui-mobile/tablet/desktop** for component patterns
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## FORBIDDEN
|
|
378
|
+
|
|
379
|
+
1. **Class components** - Use function components
|
|
380
|
+
2. **Prop drilling** - Use context or composition
|
|
381
|
+
3. **Inline objects/functions in JSX** - Causes re-renders
|
|
382
|
+
4. **useEffect for derived state** - Use useMemo
|
|
383
|
+
5. **Mutating state directly** - Always use setState/dispatch
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Version
|
|
388
|
+
|
|
389
|
+
- **v1.0.0** - Initial implementation based on React 19 patterns
|