omgkit 2.1.1 → 2.2.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.
Files changed (50) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/SKILL_STANDARDS.md +743 -0
  3. package/plugin/skills/databases/mongodb/SKILL.md +797 -28
  4. package/plugin/skills/databases/prisma/SKILL.md +776 -30
  5. package/plugin/skills/databases/redis/SKILL.md +885 -25
  6. package/plugin/skills/devops/aws/SKILL.md +686 -28
  7. package/plugin/skills/devops/github-actions/SKILL.md +684 -29
  8. package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
  9. package/plugin/skills/frameworks/django/SKILL.md +920 -20
  10. package/plugin/skills/frameworks/express/SKILL.md +1361 -35
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
  12. package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
  14. package/plugin/skills/frameworks/rails/SKILL.md +594 -28
  15. package/plugin/skills/frameworks/spring/SKILL.md +528 -35
  16. package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
  17. package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
  18. package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
  19. package/plugin/skills/frontend/responsive/SKILL.md +847 -21
  20. package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
  21. package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
  22. package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
  23. package/plugin/skills/languages/javascript/SKILL.md +935 -31
  24. package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
  25. package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
  26. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
  27. package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
  28. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
  29. package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
  30. package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
  31. package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
  32. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
  33. package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
  34. package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
  35. package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
  36. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
  37. package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
  38. package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
  39. package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
  40. package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
  41. package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
  42. package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
  43. package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
  44. package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
  45. package/plugin/skills/security/better-auth/SKILL.md +1065 -28
  46. package/plugin/skills/security/oauth/SKILL.md +968 -31
  47. package/plugin/skills/security/owasp/SKILL.md +894 -33
  48. package/plugin/skills/testing/playwright/SKILL.md +764 -38
  49. package/plugin/skills/testing/pytest/SKILL.md +873 -36
  50. package/plugin/skills/testing/vitest/SKILL.md +980 -35
@@ -1,47 +1,1325 @@
1
1
  ---
2
2
  name: frontend-design
3
- description: Frontend design patterns. Use for component architecture, state management.
3
+ description: Frontend design patterns with compound components, render props, custom hooks, state machines, and scalable architecture
4
+ category: frontend
5
+ triggers:
6
+ - frontend design
7
+ - component patterns
8
+ - design patterns
9
+ - compound components
10
+ - render props
11
+ - custom hooks
12
+ - state management
13
+ - component architecture
4
14
  ---
5
15
 
6
- # Frontend Design Skill
16
+ # Frontend Design Patterns
7
17
 
8
- ## Component Patterns
18
+ Enterprise-grade **frontend design patterns** following industry best practices. This skill covers compound components, render props, custom hooks, state machines, higher-order components, and scalable architecture patterns used by top engineering teams.
19
+
20
+ ## Purpose
21
+
22
+ Build maintainable and scalable frontend applications:
23
+
24
+ - Design reusable component APIs with compound patterns
25
+ - Implement flexible data fetching with render props
26
+ - Create powerful custom hooks for shared logic
27
+ - Manage complex state with state machines
28
+ - Build accessible form systems
29
+ - Implement optimistic UI updates
30
+ - Design scalable component architecture
31
+
32
+ ## Features
33
+
34
+ ### 1. Compound Components Pattern
9
35
 
10
- ### Compound Components
11
36
  ```tsx
12
- <Select>
13
- <SelectTrigger>Select option</SelectTrigger>
14
- <SelectContent>
15
- <SelectItem value="1">Option 1</SelectItem>
16
- </SelectContent>
17
- </Select>
37
+ // components/Select/SelectContext.tsx
38
+ import { createContext, useContext, useState, useCallback, ReactNode } from 'react';
39
+
40
+ interface SelectContextValue {
41
+ isOpen: boolean;
42
+ selectedValue: string | null;
43
+ selectedLabel: string | null;
44
+ highlightedIndex: number;
45
+ open: () => void;
46
+ close: () => void;
47
+ toggle: () => void;
48
+ selectOption: (value: string, label: string) => void;
49
+ setHighlightedIndex: (index: number) => void;
50
+ }
51
+
52
+ const SelectContext = createContext<SelectContextValue | null>(null);
53
+
54
+ function useSelectContext() {
55
+ const context = useContext(SelectContext);
56
+ if (!context) {
57
+ throw new Error('Select components must be used within a Select provider');
58
+ }
59
+ return context;
60
+ }
61
+
62
+ // components/Select/Select.tsx
63
+ interface SelectProps {
64
+ children: ReactNode;
65
+ defaultValue?: string;
66
+ value?: string;
67
+ onValueChange?: (value: string) => void;
68
+ disabled?: boolean;
69
+ }
70
+
71
+ export function Select({
72
+ children,
73
+ defaultValue,
74
+ value: controlledValue,
75
+ onValueChange,
76
+ disabled = false,
77
+ }: SelectProps) {
78
+ const [isOpen, setIsOpen] = useState(false);
79
+ const [internalValue, setInternalValue] = useState(defaultValue ?? null);
80
+ const [selectedLabel, setSelectedLabel] = useState<string | null>(null);
81
+ const [highlightedIndex, setHighlightedIndex] = useState(-1);
82
+
83
+ const isControlled = controlledValue !== undefined;
84
+ const selectedValue = isControlled ? controlledValue : internalValue;
85
+
86
+ const open = useCallback(() => {
87
+ if (!disabled) setIsOpen(true);
88
+ }, [disabled]);
89
+
90
+ const close = useCallback(() => {
91
+ setIsOpen(false);
92
+ setHighlightedIndex(-1);
93
+ }, []);
94
+
95
+ const toggle = useCallback(() => {
96
+ if (!disabled) setIsOpen((prev) => !prev);
97
+ }, [disabled]);
98
+
99
+ const selectOption = useCallback(
100
+ (value: string, label: string) => {
101
+ if (!isControlled) {
102
+ setInternalValue(value);
103
+ }
104
+ setSelectedLabel(label);
105
+ onValueChange?.(value);
106
+ close();
107
+ },
108
+ [isControlled, onValueChange, close]
109
+ );
110
+
111
+ return (
112
+ <SelectContext.Provider
113
+ value={{
114
+ isOpen,
115
+ selectedValue,
116
+ selectedLabel,
117
+ highlightedIndex,
118
+ open,
119
+ close,
120
+ toggle,
121
+ selectOption,
122
+ setHighlightedIndex,
123
+ }}
124
+ >
125
+ <div className="relative inline-block" data-disabled={disabled}>
126
+ {children}
127
+ </div>
128
+ </SelectContext.Provider>
129
+ );
130
+ }
131
+
132
+ // components/Select/SelectTrigger.tsx
133
+ interface SelectTriggerProps {
134
+ children?: ReactNode;
135
+ placeholder?: string;
136
+ className?: string;
137
+ }
138
+
139
+ export function SelectTrigger({
140
+ children,
141
+ placeholder = 'Select an option',
142
+ className = '',
143
+ }: SelectTriggerProps) {
144
+ const { isOpen, selectedLabel, toggle } = useSelectContext();
145
+
146
+ return (
147
+ <button
148
+ type="button"
149
+ role="combobox"
150
+ aria-expanded={isOpen}
151
+ aria-haspopup="listbox"
152
+ className={`flex items-center justify-between gap-2 px-3 py-2 border rounded-md
153
+ bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500
154
+ ${className}`}
155
+ onClick={toggle}
156
+ >
157
+ <span className={selectedLabel ? 'text-gray-900' : 'text-gray-500'}>
158
+ {selectedLabel || children || placeholder}
159
+ </span>
160
+ <ChevronIcon isOpen={isOpen} />
161
+ </button>
162
+ );
163
+ }
164
+
165
+ // components/Select/SelectContent.tsx
166
+ interface SelectContentProps {
167
+ children: ReactNode;
168
+ className?: string;
169
+ }
170
+
171
+ export function SelectContent({ children, className = '' }: SelectContentProps) {
172
+ const { isOpen, close } = useSelectContext();
173
+ const contentRef = useRef<HTMLDivElement>(null);
174
+
175
+ // Close on outside click
176
+ useEffect(() => {
177
+ const handleClickOutside = (event: MouseEvent) => {
178
+ if (contentRef.current && !contentRef.current.contains(event.target as Node)) {
179
+ close();
180
+ }
181
+ };
182
+
183
+ if (isOpen) {
184
+ document.addEventListener('mousedown', handleClickOutside);
185
+ return () => document.removeEventListener('mousedown', handleClickOutside);
186
+ }
187
+ }, [isOpen, close]);
188
+
189
+ // Close on escape
190
+ useEffect(() => {
191
+ const handleEscape = (event: KeyboardEvent) => {
192
+ if (event.key === 'Escape') close();
193
+ };
194
+
195
+ if (isOpen) {
196
+ document.addEventListener('keydown', handleEscape);
197
+ return () => document.removeEventListener('keydown', handleEscape);
198
+ }
199
+ }, [isOpen, close]);
200
+
201
+ if (!isOpen) return null;
202
+
203
+ return (
204
+ <div
205
+ ref={contentRef}
206
+ role="listbox"
207
+ className={`absolute z-50 mt-1 w-full bg-white border rounded-md shadow-lg
208
+ max-h-60 overflow-auto ${className}`}
209
+ >
210
+ {children}
211
+ </div>
212
+ );
213
+ }
214
+
215
+ // components/Select/SelectItem.tsx
216
+ interface SelectItemProps {
217
+ value: string;
218
+ children: ReactNode;
219
+ disabled?: boolean;
220
+ }
221
+
222
+ export function SelectItem({ value, children, disabled = false }: SelectItemProps) {
223
+ const { selectedValue, selectOption } = useSelectContext();
224
+ const isSelected = selectedValue === value;
225
+
226
+ const handleSelect = () => {
227
+ if (!disabled) {
228
+ selectOption(value, typeof children === 'string' ? children : value);
229
+ }
230
+ };
231
+
232
+ return (
233
+ <div
234
+ role="option"
235
+ aria-selected={isSelected}
236
+ aria-disabled={disabled}
237
+ className={`px-3 py-2 cursor-pointer
238
+ ${isSelected ? 'bg-blue-100 text-blue-900' : 'text-gray-900'}
239
+ ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-100'}
240
+ `}
241
+ onClick={handleSelect}
242
+ >
243
+ {children}
244
+ </div>
245
+ );
246
+ }
247
+
248
+ // Usage example
249
+ function SelectExample() {
250
+ const [country, setCountry] = useState('');
251
+
252
+ return (
253
+ <Select value={country} onValueChange={setCountry}>
254
+ <SelectTrigger placeholder="Select a country" />
255
+ <SelectContent>
256
+ <SelectItem value="us">United States</SelectItem>
257
+ <SelectItem value="uk">United Kingdom</SelectItem>
258
+ <SelectItem value="ca">Canada</SelectItem>
259
+ <SelectItem value="au" disabled>Australia (Coming Soon)</SelectItem>
260
+ </SelectContent>
261
+ </Select>
262
+ );
263
+ }
18
264
  ```
19
265
 
20
- ### Render Props
266
+ ### 2. Render Props Pattern
267
+
21
268
  ```tsx
22
- <DataFetcher url="/api/users">
23
- {({ data, loading }) => loading ? <Spinner /> : <UserList users={data} />}
24
- </DataFetcher>
269
+ // components/DataFetcher.tsx
270
+ import { useState, useEffect, useCallback, ReactNode } from 'react';
271
+
272
+ interface FetchState<T> {
273
+ data: T | null;
274
+ loading: boolean;
275
+ error: Error | null;
276
+ refetch: () => Promise<void>;
277
+ }
278
+
279
+ interface DataFetcherProps<T> {
280
+ url: string;
281
+ options?: RequestInit;
282
+ children: (state: FetchState<T>) => ReactNode;
283
+ onSuccess?: (data: T) => void;
284
+ onError?: (error: Error) => void;
285
+ initialData?: T;
286
+ enabled?: boolean;
287
+ }
288
+
289
+ export function DataFetcher<T>({
290
+ url,
291
+ options,
292
+ children,
293
+ onSuccess,
294
+ onError,
295
+ initialData = null as T,
296
+ enabled = true,
297
+ }: DataFetcherProps<T>) {
298
+ const [data, setData] = useState<T | null>(initialData);
299
+ const [loading, setLoading] = useState(false);
300
+ const [error, setError] = useState<Error | null>(null);
301
+
302
+ const fetchData = useCallback(async () => {
303
+ if (!enabled) return;
304
+
305
+ setLoading(true);
306
+ setError(null);
307
+
308
+ try {
309
+ const response = await fetch(url, {
310
+ ...options,
311
+ headers: {
312
+ 'Content-Type': 'application/json',
313
+ ...options?.headers,
314
+ },
315
+ });
316
+
317
+ if (!response.ok) {
318
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
319
+ }
320
+
321
+ const result = await response.json();
322
+ setData(result);
323
+ onSuccess?.(result);
324
+ } catch (err) {
325
+ const error = err instanceof Error ? err : new Error(String(err));
326
+ setError(error);
327
+ onError?.(error);
328
+ } finally {
329
+ setLoading(false);
330
+ }
331
+ }, [url, options, enabled, onSuccess, onError]);
332
+
333
+ useEffect(() => {
334
+ fetchData();
335
+ }, [fetchData]);
336
+
337
+ return <>{children({ data, loading, error, refetch: fetchData })}</>;
338
+ }
339
+
340
+ // Mouse position render props
341
+ interface MousePosition {
342
+ x: number;
343
+ y: number;
344
+ }
345
+
346
+ interface MouseTrackerProps {
347
+ children: (position: MousePosition) => ReactNode;
348
+ }
349
+
350
+ export function MouseTracker({ children }: MouseTrackerProps) {
351
+ const [position, setPosition] = useState<MousePosition>({ x: 0, y: 0 });
352
+
353
+ useEffect(() => {
354
+ const handleMouseMove = (event: MouseEvent) => {
355
+ setPosition({ x: event.clientX, y: event.clientY });
356
+ };
357
+
358
+ window.addEventListener('mousemove', handleMouseMove);
359
+ return () => window.removeEventListener('mousemove', handleMouseMove);
360
+ }, []);
361
+
362
+ return <>{children(position)}</>;
363
+ }
364
+
365
+ // Intersection observer render props
366
+ interface IntersectionProps {
367
+ children: (entry: IntersectionObserverEntry | null, ref: React.RefObject<Element>) => ReactNode;
368
+ options?: IntersectionObserverInit;
369
+ }
370
+
371
+ export function IntersectionObserverRender({ children, options }: IntersectionProps) {
372
+ const [entry, setEntry] = useState<IntersectionObserverEntry | null>(null);
373
+ const ref = useRef<Element>(null);
374
+
375
+ useEffect(() => {
376
+ const element = ref.current;
377
+ if (!element) return;
378
+
379
+ const observer = new IntersectionObserver(([entry]) => {
380
+ setEntry(entry);
381
+ }, options);
382
+
383
+ observer.observe(element);
384
+ return () => observer.disconnect();
385
+ }, [options]);
386
+
387
+ return <>{children(entry, ref)}</>;
388
+ }
389
+
390
+ // Usage examples
391
+ function RenderPropsExamples() {
392
+ return (
393
+ <>
394
+ {/* Data fetcher */}
395
+ <DataFetcher<User[]> url="/api/users">
396
+ {({ data, loading, error, refetch }) => {
397
+ if (loading) return <Spinner />;
398
+ if (error) return <ErrorMessage error={error} onRetry={refetch} />;
399
+ return <UserList users={data || []} />;
400
+ }}
401
+ </DataFetcher>
402
+
403
+ {/* Mouse tracker */}
404
+ <MouseTracker>
405
+ {({ x, y }) => (
406
+ <div className="fixed pointer-events-none" style={{ left: x, top: y }}>
407
+ Custom cursor at ({x}, {y})
408
+ </div>
409
+ )}
410
+ </MouseTracker>
411
+
412
+ {/* Lazy loading with intersection observer */}
413
+ <IntersectionObserverRender options={{ threshold: 0.1 }}>
414
+ {(entry, ref) => (
415
+ <div ref={ref as React.RefObject<HTMLDivElement>}>
416
+ {entry?.isIntersecting ? <HeavyComponent /> : <Placeholder />}
417
+ </div>
418
+ )}
419
+ </IntersectionObserverRender>
420
+ </>
421
+ );
422
+ }
25
423
  ```
26
424
 
27
- ### Custom Hooks
425
+ ### 3. Custom Hooks Pattern
426
+
28
427
  ```tsx
29
- function useLocalStorage<T>(key: string, initial: T) {
30
- const [value, setValue] = useState<T>(() => {
31
- const stored = localStorage.getItem(key);
32
- return stored ? JSON.parse(stored) : initial;
428
+ // hooks/useLocalStorage.ts
429
+ import { useState, useEffect, useCallback } from 'react';
430
+
431
+ export function useLocalStorage<T>(
432
+ key: string,
433
+ initialValue: T
434
+ ): [T, (value: T | ((prev: T) => T)) => void, () => void] {
435
+ // Get initial value from localStorage or use provided initial
436
+ const [storedValue, setStoredValue] = useState<T>(() => {
437
+ if (typeof window === 'undefined') return initialValue;
438
+
439
+ try {
440
+ const item = window.localStorage.getItem(key);
441
+ return item ? JSON.parse(item) : initialValue;
442
+ } catch (error) {
443
+ console.warn(`Error reading localStorage key "${key}":`, error);
444
+ return initialValue;
445
+ }
33
446
  });
34
447
 
448
+ // Persist to localStorage when value changes
449
+ useEffect(() => {
450
+ if (typeof window === 'undefined') return;
451
+
452
+ try {
453
+ window.localStorage.setItem(key, JSON.stringify(storedValue));
454
+ } catch (error) {
455
+ console.warn(`Error setting localStorage key "${key}":`, error);
456
+ }
457
+ }, [key, storedValue]);
458
+
459
+ // Listen for changes in other tabs/windows
460
+ useEffect(() => {
461
+ const handleStorageChange = (event: StorageEvent) => {
462
+ if (event.key === key && event.newValue) {
463
+ try {
464
+ setStoredValue(JSON.parse(event.newValue));
465
+ } catch {
466
+ // Ignore parse errors
467
+ }
468
+ }
469
+ };
470
+
471
+ window.addEventListener('storage', handleStorageChange);
472
+ return () => window.removeEventListener('storage', handleStorageChange);
473
+ }, [key]);
474
+
475
+ const remove = useCallback(() => {
476
+ if (typeof window !== 'undefined') {
477
+ window.localStorage.removeItem(key);
478
+ setStoredValue(initialValue);
479
+ }
480
+ }, [key, initialValue]);
481
+
482
+ return [storedValue, setStoredValue, remove];
483
+ }
484
+
485
+ // hooks/useDebounce.ts
486
+ export function useDebounce<T>(value: T, delay: number): T {
487
+ const [debouncedValue, setDebouncedValue] = useState(value);
488
+
35
489
  useEffect(() => {
36
- localStorage.setItem(key, JSON.stringify(value));
37
- }, [key, value]);
490
+ const timer = setTimeout(() => {
491
+ setDebouncedValue(value);
492
+ }, delay);
38
493
 
39
- return [value, setValue] as const;
494
+ return () => clearTimeout(timer);
495
+ }, [value, delay]);
496
+
497
+ return debouncedValue;
498
+ }
499
+
500
+ // hooks/useDebouncedCallback.ts
501
+ export function useDebouncedCallback<T extends (...args: any[]) => any>(
502
+ callback: T,
503
+ delay: number
504
+ ): (...args: Parameters<T>) => void {
505
+ const timeoutRef = useRef<NodeJS.Timeout | null>(null);
506
+ const callbackRef = useRef(callback);
507
+
508
+ // Keep callback ref updated
509
+ useEffect(() => {
510
+ callbackRef.current = callback;
511
+ }, [callback]);
512
+
513
+ // Cleanup on unmount
514
+ useEffect(() => {
515
+ return () => {
516
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
517
+ };
518
+ }, []);
519
+
520
+ return useCallback(
521
+ (...args: Parameters<T>) => {
522
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
523
+
524
+ timeoutRef.current = setTimeout(() => {
525
+ callbackRef.current(...args);
526
+ }, delay);
527
+ },
528
+ [delay]
529
+ );
530
+ }
531
+
532
+ // hooks/useAsync.ts
533
+ interface AsyncState<T> {
534
+ data: T | null;
535
+ loading: boolean;
536
+ error: Error | null;
537
+ }
538
+
539
+ interface UseAsyncReturn<T> extends AsyncState<T> {
540
+ execute: () => Promise<T | null>;
541
+ reset: () => void;
542
+ }
543
+
544
+ export function useAsync<T>(
545
+ asyncFunction: () => Promise<T>,
546
+ immediate = true
547
+ ): UseAsyncReturn<T> {
548
+ const [state, setState] = useState<AsyncState<T>>({
549
+ data: null,
550
+ loading: immediate,
551
+ error: null,
552
+ });
553
+
554
+ const execute = useCallback(async () => {
555
+ setState((prev) => ({ ...prev, loading: true, error: null }));
556
+
557
+ try {
558
+ const data = await asyncFunction();
559
+ setState({ data, loading: false, error: null });
560
+ return data;
561
+ } catch (error) {
562
+ setState({
563
+ data: null,
564
+ loading: false,
565
+ error: error instanceof Error ? error : new Error(String(error)),
566
+ });
567
+ return null;
568
+ }
569
+ }, [asyncFunction]);
570
+
571
+ const reset = useCallback(() => {
572
+ setState({ data: null, loading: false, error: null });
573
+ }, []);
574
+
575
+ useEffect(() => {
576
+ if (immediate) {
577
+ execute();
578
+ }
579
+ }, [execute, immediate]);
580
+
581
+ return { ...state, execute, reset };
582
+ }
583
+
584
+ // hooks/useMediaQuery.ts
585
+ export function useMediaQuery(query: string): boolean {
586
+ const [matches, setMatches] = useState(() => {
587
+ if (typeof window === 'undefined') return false;
588
+ return window.matchMedia(query).matches;
589
+ });
590
+
591
+ useEffect(() => {
592
+ const mediaQuery = window.matchMedia(query);
593
+ const handler = (event: MediaQueryListEvent) => setMatches(event.matches);
594
+
595
+ // Set initial value
596
+ setMatches(mediaQuery.matches);
597
+
598
+ // Modern browsers
599
+ mediaQuery.addEventListener('change', handler);
600
+ return () => mediaQuery.removeEventListener('change', handler);
601
+ }, [query]);
602
+
603
+ return matches;
604
+ }
605
+
606
+ // hooks/useClickOutside.ts
607
+ export function useClickOutside<T extends HTMLElement>(
608
+ callback: () => void
609
+ ): React.RefObject<T> {
610
+ const ref = useRef<T>(null);
611
+
612
+ useEffect(() => {
613
+ const handleClick = (event: MouseEvent) => {
614
+ if (ref.current && !ref.current.contains(event.target as Node)) {
615
+ callback();
616
+ }
617
+ };
618
+
619
+ document.addEventListener('mousedown', handleClick);
620
+ return () => document.removeEventListener('mousedown', handleClick);
621
+ }, [callback]);
622
+
623
+ return ref;
624
+ }
625
+
626
+ // hooks/usePrevious.ts
627
+ export function usePrevious<T>(value: T): T | undefined {
628
+ const ref = useRef<T>();
629
+
630
+ useEffect(() => {
631
+ ref.current = value;
632
+ }, [value]);
633
+
634
+ return ref.current;
635
+ }
636
+
637
+ // Usage examples
638
+ function HooksExamples() {
639
+ const [theme, setTheme] = useLocalStorage('theme', 'light');
640
+ const [search, setSearch] = useState('');
641
+ const debouncedSearch = useDebounce(search, 300);
642
+ const isMobile = useMediaQuery('(max-width: 768px)');
643
+ const { data, loading, error } = useAsync(() => fetchUsers());
644
+
645
+ const dropdownRef = useClickOutside<HTMLDivElement>(() => {
646
+ setIsOpen(false);
647
+ });
648
+
649
+ return (
650
+ <div>
651
+ <ThemeToggle theme={theme} onChange={setTheme} />
652
+ <SearchInput value={search} onChange={setSearch} />
653
+ <SearchResults query={debouncedSearch} />
654
+ {isMobile ? <MobileNav /> : <DesktopNav />}
655
+ </div>
656
+ );
657
+ }
658
+ ```
659
+
660
+ ### 4. State Machine Pattern
661
+
662
+ ```tsx
663
+ // lib/createStateMachine.ts
664
+ type StateConfig<TContext, TState extends string, TEvent extends { type: string }> = {
665
+ initial: TState;
666
+ context: TContext;
667
+ states: {
668
+ [K in TState]: {
669
+ on?: {
670
+ [E in TEvent['type']]?: TState | {
671
+ target: TState;
672
+ actions?: (context: TContext, event: Extract<TEvent, { type: E }>) => TContext;
673
+ guard?: (context: TContext, event: Extract<TEvent, { type: E }>) => boolean;
674
+ };
675
+ };
676
+ entry?: (context: TContext) => TContext | void;
677
+ exit?: (context: TContext) => TContext | void;
678
+ };
679
+ };
680
+ };
681
+
682
+ // hooks/useStateMachine.ts
683
+ export function useStateMachine<
684
+ TContext,
685
+ TState extends string,
686
+ TEvent extends { type: string }
687
+ >(config: StateConfig<TContext, TState, TEvent>) {
688
+ const [state, setState] = useState<TState>(config.initial);
689
+ const [context, setContext] = useState<TContext>(config.context);
690
+
691
+ const send = useCallback(
692
+ (event: TEvent) => {
693
+ const currentStateConfig = config.states[state];
694
+ const transition = currentStateConfig.on?.[event.type as TEvent['type']];
695
+
696
+ if (!transition) return;
697
+
698
+ let nextState: TState;
699
+ let newContext = context;
700
+
701
+ if (typeof transition === 'string') {
702
+ nextState = transition;
703
+ } else {
704
+ // Check guard condition
705
+ if (transition.guard && !transition.guard(context, event as any)) {
706
+ return;
707
+ }
708
+ nextState = transition.target;
709
+ if (transition.actions) {
710
+ newContext = transition.actions(context, event as any);
711
+ }
712
+ }
713
+
714
+ // Run exit action
715
+ const exitAction = currentStateConfig.exit;
716
+ if (exitAction) {
717
+ const result = exitAction(newContext);
718
+ if (result) newContext = result;
719
+ }
720
+
721
+ // Run entry action
722
+ const nextStateConfig = config.states[nextState];
723
+ const entryAction = nextStateConfig.entry;
724
+ if (entryAction) {
725
+ const result = entryAction(newContext);
726
+ if (result) newContext = result;
727
+ }
728
+
729
+ setState(nextState);
730
+ setContext(newContext);
731
+ },
732
+ [state, context, config]
733
+ );
734
+
735
+ const matches = useCallback((s: TState) => state === s, [state]);
736
+
737
+ return { state, context, send, matches };
738
+ }
739
+
740
+ // Example: Form submission state machine
741
+ type FormState = 'idle' | 'validating' | 'submitting' | 'success' | 'error';
742
+
743
+ type FormEvent =
744
+ | { type: 'SUBMIT'; data: FormData }
745
+ | { type: 'VALIDATE_SUCCESS' }
746
+ | { type: 'VALIDATE_ERROR'; errors: Record<string, string> }
747
+ | { type: 'SUBMIT_SUCCESS'; response: any }
748
+ | { type: 'SUBMIT_ERROR'; error: string }
749
+ | { type: 'RESET' };
750
+
751
+ interface FormContext {
752
+ data: FormData | null;
753
+ errors: Record<string, string>;
754
+ response: any;
755
+ submitError: string | null;
756
+ }
757
+
758
+ const formMachine: StateConfig<FormContext, FormState, FormEvent> = {
759
+ initial: 'idle',
760
+ context: {
761
+ data: null,
762
+ errors: {},
763
+ response: null,
764
+ submitError: null,
765
+ },
766
+ states: {
767
+ idle: {
768
+ on: {
769
+ SUBMIT: {
770
+ target: 'validating',
771
+ actions: (ctx, event) => ({ ...ctx, data: event.data, errors: {} }),
772
+ },
773
+ },
774
+ },
775
+ validating: {
776
+ on: {
777
+ VALIDATE_SUCCESS: 'submitting',
778
+ VALIDATE_ERROR: {
779
+ target: 'idle',
780
+ actions: (ctx, event) => ({ ...ctx, errors: event.errors }),
781
+ },
782
+ },
783
+ },
784
+ submitting: {
785
+ on: {
786
+ SUBMIT_SUCCESS: {
787
+ target: 'success',
788
+ actions: (ctx, event) => ({ ...ctx, response: event.response }),
789
+ },
790
+ SUBMIT_ERROR: {
791
+ target: 'error',
792
+ actions: (ctx, event) => ({ ...ctx, submitError: event.error }),
793
+ },
794
+ },
795
+ },
796
+ success: {
797
+ on: {
798
+ RESET: {
799
+ target: 'idle',
800
+ actions: () => ({
801
+ data: null,
802
+ errors: {},
803
+ response: null,
804
+ submitError: null,
805
+ }),
806
+ },
807
+ },
808
+ },
809
+ error: {
810
+ on: {
811
+ SUBMIT: {
812
+ target: 'validating',
813
+ actions: (ctx, event) => ({
814
+ ...ctx,
815
+ data: event.data,
816
+ submitError: null,
817
+ }),
818
+ },
819
+ RESET: {
820
+ target: 'idle',
821
+ actions: () => ({
822
+ data: null,
823
+ errors: {},
824
+ response: null,
825
+ submitError: null,
826
+ }),
827
+ },
828
+ },
829
+ },
830
+ },
831
+ };
832
+
833
+ function FormWithStateMachine() {
834
+ const { state, context, send, matches } = useStateMachine(formMachine);
835
+
836
+ const handleSubmit = async (formData: FormData) => {
837
+ send({ type: 'SUBMIT', data: formData });
838
+
839
+ // Validate
840
+ const errors = validate(formData);
841
+ if (Object.keys(errors).length > 0) {
842
+ send({ type: 'VALIDATE_ERROR', errors });
843
+ return;
844
+ }
845
+ send({ type: 'VALIDATE_SUCCESS' });
846
+
847
+ // Submit
848
+ try {
849
+ const response = await submitForm(formData);
850
+ send({ type: 'SUBMIT_SUCCESS', response });
851
+ } catch (error) {
852
+ send({ type: 'SUBMIT_ERROR', error: error.message });
853
+ }
854
+ };
855
+
856
+ return (
857
+ <form onSubmit={(e) => { e.preventDefault(); handleSubmit(new FormData(e.target)); }}>
858
+ {matches('success') && <SuccessMessage response={context.response} />}
859
+ {matches('error') && <ErrorMessage error={context.submitError} />}
860
+ {Object.entries(context.errors).map(([field, error]) => (
861
+ <FieldError key={field} field={field} error={error} />
862
+ ))}
863
+ <button type="submit" disabled={matches('submitting') || matches('validating')}>
864
+ {matches('submitting') ? 'Submitting...' : 'Submit'}
865
+ </button>
866
+ </form>
867
+ );
868
+ }
869
+ ```
870
+
871
+ ### 5. Higher-Order Components (HOC)
872
+
873
+ ```tsx
874
+ // hocs/withAuth.tsx
875
+ import { useRouter } from 'next/router';
876
+ import { ComponentType, useEffect } from 'react';
877
+ import { useAuth } from '@/hooks/useAuth';
878
+
879
+ interface WithAuthOptions {
880
+ redirectTo?: string;
881
+ requiredRole?: string;
882
+ }
883
+
884
+ export function withAuth<P extends object>(
885
+ WrappedComponent: ComponentType<P>,
886
+ options: WithAuthOptions = {}
887
+ ) {
888
+ const { redirectTo = '/login', requiredRole } = options;
889
+
890
+ function AuthenticatedComponent(props: P) {
891
+ const router = useRouter();
892
+ const { user, loading, isAuthenticated } = useAuth();
893
+
894
+ useEffect(() => {
895
+ if (!loading && !isAuthenticated) {
896
+ router.push(`${redirectTo}?returnUrl=${encodeURIComponent(router.asPath)}`);
897
+ }
898
+
899
+ if (!loading && requiredRole && user?.role !== requiredRole) {
900
+ router.push('/unauthorized');
901
+ }
902
+ }, [loading, isAuthenticated, user, router]);
903
+
904
+ if (loading) {
905
+ return <LoadingSpinner />;
906
+ }
907
+
908
+ if (!isAuthenticated) {
909
+ return null;
910
+ }
911
+
912
+ if (requiredRole && user?.role !== requiredRole) {
913
+ return null;
914
+ }
915
+
916
+ return <WrappedComponent {...props} />;
917
+ }
918
+
919
+ AuthenticatedComponent.displayName = `withAuth(${
920
+ WrappedComponent.displayName || WrappedComponent.name || 'Component'
921
+ })`;
922
+
923
+ return AuthenticatedComponent;
924
+ }
925
+
926
+ // hocs/withErrorBoundary.tsx
927
+ interface WithErrorBoundaryOptions {
928
+ fallback?: ReactNode;
929
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
930
+ }
931
+
932
+ export function withErrorBoundary<P extends object>(
933
+ WrappedComponent: ComponentType<P>,
934
+ options: WithErrorBoundaryOptions = {}
935
+ ) {
936
+ const { fallback, onError } = options;
937
+
938
+ class ErrorBoundaryHOC extends React.Component<
939
+ P,
940
+ { hasError: boolean; error: Error | null }
941
+ > {
942
+ static displayName = `withErrorBoundary(${
943
+ WrappedComponent.displayName || WrappedComponent.name || 'Component'
944
+ })`;
945
+
946
+ constructor(props: P) {
947
+ super(props);
948
+ this.state = { hasError: false, error: null };
949
+ }
950
+
951
+ static getDerivedStateFromError(error: Error) {
952
+ return { hasError: true, error };
953
+ }
954
+
955
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
956
+ onError?.(error, errorInfo);
957
+ }
958
+
959
+ render() {
960
+ if (this.state.hasError) {
961
+ return fallback || <DefaultErrorFallback error={this.state.error} />;
962
+ }
963
+
964
+ return <WrappedComponent {...this.props} />;
965
+ }
966
+ }
967
+
968
+ return ErrorBoundaryHOC;
969
+ }
970
+
971
+ // hocs/withLoading.tsx
972
+ interface WithLoadingProps {
973
+ isLoading?: boolean;
974
+ }
975
+
976
+ export function withLoading<P extends object>(
977
+ WrappedComponent: ComponentType<P>,
978
+ LoadingComponent: ComponentType = DefaultSpinner
979
+ ) {
980
+ function LoadingWrapper(props: P & WithLoadingProps) {
981
+ const { isLoading, ...rest } = props;
982
+
983
+ if (isLoading) {
984
+ return <LoadingComponent />;
985
+ }
986
+
987
+ return <WrappedComponent {...(rest as P)} />;
988
+ }
989
+
990
+ LoadingWrapper.displayName = `withLoading(${
991
+ WrappedComponent.displayName || WrappedComponent.name || 'Component'
992
+ })`;
993
+
994
+ return LoadingWrapper;
995
+ }
996
+
997
+ // Usage
998
+ const ProtectedDashboard = withAuth(Dashboard, { requiredRole: 'admin' });
999
+ const SafeUserProfile = withErrorBoundary(UserProfile, {
1000
+ fallback: <div>Something went wrong</div>,
1001
+ });
1002
+ const LoadableDataGrid = withLoading(DataGrid);
1003
+ ```
1004
+
1005
+ ### 6. Optimistic UI Updates
1006
+
1007
+ ```tsx
1008
+ // hooks/useOptimistic.ts
1009
+ interface OptimisticState<T> {
1010
+ data: T;
1011
+ pending: boolean;
1012
+ error: Error | null;
1013
+ }
1014
+
1015
+ export function useOptimistic<T, TAction>(
1016
+ initialData: T,
1017
+ reducer: (state: T, action: TAction) => T
1018
+ ) {
1019
+ const [state, setState] = useState<OptimisticState<T>>({
1020
+ data: initialData,
1021
+ pending: false,
1022
+ error: null,
1023
+ });
1024
+
1025
+ const previousDataRef = useRef<T>(initialData);
1026
+
1027
+ const optimisticUpdate = useCallback(
1028
+ async (action: TAction, asyncOperation: () => Promise<T>) => {
1029
+ // Store previous state for rollback
1030
+ previousDataRef.current = state.data;
1031
+
1032
+ // Apply optimistic update
1033
+ const optimisticData = reducer(state.data, action);
1034
+ setState({ data: optimisticData, pending: true, error: null });
1035
+
1036
+ try {
1037
+ // Perform actual async operation
1038
+ const result = await asyncOperation();
1039
+ setState({ data: result, pending: false, error: null });
1040
+ return result;
1041
+ } catch (error) {
1042
+ // Rollback on error
1043
+ setState({
1044
+ data: previousDataRef.current,
1045
+ pending: false,
1046
+ error: error instanceof Error ? error : new Error(String(error)),
1047
+ });
1048
+ throw error;
1049
+ }
1050
+ },
1051
+ [state.data, reducer]
1052
+ );
1053
+
1054
+ return { ...state, optimisticUpdate };
1055
+ }
1056
+
1057
+ // Example: Todo list with optimistic updates
1058
+ interface Todo {
1059
+ id: string;
1060
+ text: string;
1061
+ completed: boolean;
1062
+ }
1063
+
1064
+ function TodoList() {
1065
+ const { data: todos, pending, error, optimisticUpdate } = useOptimistic<
1066
+ Todo[],
1067
+ { type: 'ADD' | 'TOGGLE' | 'DELETE'; payload: any }
1068
+ >(initialTodos, (state, action) => {
1069
+ switch (action.type) {
1070
+ case 'ADD':
1071
+ return [...state, action.payload];
1072
+ case 'TOGGLE':
1073
+ return state.map((todo) =>
1074
+ todo.id === action.payload
1075
+ ? { ...todo, completed: !todo.completed }
1076
+ : todo
1077
+ );
1078
+ case 'DELETE':
1079
+ return state.filter((todo) => todo.id !== action.payload);
1080
+ default:
1081
+ return state;
1082
+ }
1083
+ });
1084
+
1085
+ const addTodo = async (text: string) => {
1086
+ const tempId = `temp-${Date.now()}`;
1087
+ const newTodo = { id: tempId, text, completed: false };
1088
+
1089
+ await optimisticUpdate(
1090
+ { type: 'ADD', payload: newTodo },
1091
+ async () => {
1092
+ const response = await api.createTodo({ text });
1093
+ return todos.map((t) => (t.id === tempId ? response : t));
1094
+ }
1095
+ );
1096
+ };
1097
+
1098
+ const toggleTodo = async (id: string) => {
1099
+ await optimisticUpdate(
1100
+ { type: 'TOGGLE', payload: id },
1101
+ async () => {
1102
+ await api.toggleTodo(id);
1103
+ return todos.map((t) =>
1104
+ t.id === id ? { ...t, completed: !t.completed } : t
1105
+ );
1106
+ }
1107
+ );
1108
+ };
1109
+
1110
+ return (
1111
+ <div className={pending ? 'opacity-70' : ''}>
1112
+ {error && <ErrorToast message={error.message} />}
1113
+ <TodoForm onSubmit={addTodo} disabled={pending} />
1114
+ {todos.map((todo) => (
1115
+ <TodoItem
1116
+ key={todo.id}
1117
+ todo={todo}
1118
+ onToggle={() => toggleTodo(todo.id)}
1119
+ />
1120
+ ))}
1121
+ </div>
1122
+ );
1123
+ }
1124
+ ```
1125
+
1126
+ ## Use Cases
1127
+
1128
+ ### Component Library Architecture
1129
+
1130
+ ```tsx
1131
+ // lib/components/index.ts - Barrel exports
1132
+ export { Button, type ButtonProps } from './Button';
1133
+ export { Input, type InputProps } from './Input';
1134
+ export { Select, SelectTrigger, SelectContent, SelectItem } from './Select';
1135
+
1136
+ // lib/components/Button/Button.tsx
1137
+ import { forwardRef, ButtonHTMLAttributes } from 'react';
1138
+ import { cva, type VariantProps } from 'class-variance-authority';
1139
+ import { cn } from '@/lib/utils';
1140
+
1141
+ const buttonVariants = cva(
1142
+ 'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50',
1143
+ {
1144
+ variants: {
1145
+ variant: {
1146
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
1147
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
1148
+ outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
1149
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
1150
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
1151
+ link: 'text-primary underline-offset-4 hover:underline',
1152
+ },
1153
+ size: {
1154
+ default: 'h-10 px-4 py-2',
1155
+ sm: 'h-9 rounded-md px-3',
1156
+ lg: 'h-11 rounded-md px-8',
1157
+ icon: 'h-10 w-10',
1158
+ },
1159
+ },
1160
+ defaultVariants: {
1161
+ variant: 'default',
1162
+ size: 'default',
1163
+ },
1164
+ }
1165
+ );
1166
+
1167
+ export interface ButtonProps
1168
+ extends ButtonHTMLAttributes<HTMLButtonElement>,
1169
+ VariantProps<typeof buttonVariants> {
1170
+ loading?: boolean;
1171
+ }
1172
+
1173
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
1174
+ ({ className, variant, size, loading, children, disabled, ...props }, ref) => {
1175
+ return (
1176
+ <button
1177
+ className={cn(buttonVariants({ variant, size, className }))}
1178
+ ref={ref}
1179
+ disabled={disabled || loading}
1180
+ {...props}
1181
+ >
1182
+ {loading && <Spinner className="mr-2 h-4 w-4" />}
1183
+ {children}
1184
+ </button>
1185
+ );
1186
+ }
1187
+ );
1188
+
1189
+ Button.displayName = 'Button';
1190
+ ```
1191
+
1192
+ ### Form Architecture Pattern
1193
+
1194
+ ```tsx
1195
+ // hooks/useForm.ts
1196
+ interface UseFormConfig<T> {
1197
+ initialValues: T;
1198
+ validate: (values: T) => Partial<Record<keyof T, string>>;
1199
+ onSubmit: (values: T) => Promise<void>;
1200
+ }
1201
+
1202
+ export function useForm<T extends Record<string, any>>({
1203
+ initialValues,
1204
+ validate,
1205
+ onSubmit,
1206
+ }: UseFormConfig<T>) {
1207
+ const [values, setValues] = useState<T>(initialValues);
1208
+ const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({});
1209
+ const [touched, setTouched] = useState<Partial<Record<keyof T, boolean>>>({});
1210
+ const [submitting, setSubmitting] = useState(false);
1211
+
1212
+ const handleChange = useCallback(
1213
+ (field: keyof T) => (e: React.ChangeEvent<HTMLInputElement>) => {
1214
+ const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
1215
+ setValues((prev) => ({ ...prev, [field]: value }));
1216
+ },
1217
+ []
1218
+ );
1219
+
1220
+ const handleBlur = useCallback(
1221
+ (field: keyof T) => () => {
1222
+ setTouched((prev) => ({ ...prev, [field]: true }));
1223
+ const fieldErrors = validate(values);
1224
+ if (fieldErrors[field]) {
1225
+ setErrors((prev) => ({ ...prev, [field]: fieldErrors[field] }));
1226
+ } else {
1227
+ setErrors((prev) => {
1228
+ const { [field]: _, ...rest } = prev;
1229
+ return rest as Partial<Record<keyof T, string>>;
1230
+ });
1231
+ }
1232
+ },
1233
+ [values, validate]
1234
+ );
1235
+
1236
+ const handleSubmit = useCallback(
1237
+ async (e: React.FormEvent) => {
1238
+ e.preventDefault();
1239
+
1240
+ const validationErrors = validate(values);
1241
+ setErrors(validationErrors);
1242
+ setTouched(
1243
+ Object.keys(values).reduce((acc, key) => ({ ...acc, [key]: true }), {})
1244
+ );
1245
+
1246
+ if (Object.keys(validationErrors).length > 0) return;
1247
+
1248
+ setSubmitting(true);
1249
+ try {
1250
+ await onSubmit(values);
1251
+ } finally {
1252
+ setSubmitting(false);
1253
+ }
1254
+ },
1255
+ [values, validate, onSubmit]
1256
+ );
1257
+
1258
+ const reset = useCallback(() => {
1259
+ setValues(initialValues);
1260
+ setErrors({});
1261
+ setTouched({});
1262
+ }, [initialValues]);
1263
+
1264
+ const getFieldProps = useCallback(
1265
+ (field: keyof T) => ({
1266
+ value: values[field],
1267
+ onChange: handleChange(field),
1268
+ onBlur: handleBlur(field),
1269
+ error: touched[field] ? errors[field] : undefined,
1270
+ }),
1271
+ [values, errors, touched, handleChange, handleBlur]
1272
+ );
1273
+
1274
+ return {
1275
+ values,
1276
+ errors,
1277
+ touched,
1278
+ submitting,
1279
+ handleChange,
1280
+ handleBlur,
1281
+ handleSubmit,
1282
+ reset,
1283
+ getFieldProps,
1284
+ setValues,
1285
+ setFieldValue: (field: keyof T, value: T[keyof T]) =>
1286
+ setValues((prev) => ({ ...prev, [field]: value })),
1287
+ };
40
1288
  }
41
1289
  ```
42
1290
 
43
1291
  ## Best Practices
44
- - Single responsibility components
45
- - Lift state appropriately
46
- - Colocate related code
47
- - Use composition over inheritance
1292
+
1293
+ ### Do's
1294
+
1295
+ - Use compound components for complex UI with shared state
1296
+ - Create custom hooks to encapsulate reusable logic
1297
+ - Implement state machines for complex state transitions
1298
+ - Use TypeScript for type-safe component APIs
1299
+ - Prefer composition over inheritance
1300
+ - Keep components focused with single responsibility
1301
+ - Colocate related code and styles
1302
+ - Use forwardRef for component library primitives
1303
+ - Implement proper error boundaries
1304
+ - Use render props for maximum flexibility
1305
+
1306
+ ### Don'ts
1307
+
1308
+ - Don't overuse HOCs (prefer hooks)
1309
+ - Don't mutate state directly
1310
+ - Don't create deeply nested component hierarchies
1311
+ - Don't pass too many props (use context or composition)
1312
+ - Don't ignore TypeScript errors
1313
+ - Don't create components with side effects in render
1314
+ - Don't forget to memoize expensive computations
1315
+ - Don't skip accessibility in component design
1316
+ - Don't use prop drilling for deeply nested data
1317
+ - Don't forget cleanup in useEffect
1318
+
1319
+ ## References
1320
+
1321
+ - [React Patterns](https://reactpatterns.com/)
1322
+ - [Patterns.dev](https://www.patterns.dev/)
1323
+ - [Kent C. Dodds - Advanced React Patterns](https://kentcdodds.com/blog/advanced-react-patterns)
1324
+ - [XState Documentation](https://xstate.js.org/docs/)
1325
+ - [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/)