desktop-team-doc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/content/docs/README.md +227 -0
- package/content/docs/index.md +352 -0
- package/content/docs/instructions/coding-conventions/.clang-format +65 -0
- package/content/docs/instructions/coding-conventions/cpp.md +132 -0
- package/content/docs/instructions/coding-conventions/frontend.md +612 -0
- package/content/docs/instructions/coding-conventions/team-wide.md +176 -0
- package/content/docs/instructions/workflows/assets/jira-1.png +0 -0
- package/content/docs/instructions/workflows/assets/jira-comment.png +0 -0
- package/content/docs/instructions/workflows/assets/jira-release-note.png +0 -0
- package/content/docs/instructions/workflows/assets/jira-tag.png +0 -0
- package/content/docs/instructions/workflows/code-review.md +451 -0
- package/content/docs/instructions/workflows/git-branch-convention.md +246 -0
- package/content/docs/instructions/workflows/git-commit.md +95 -0
- package/content/docs/instructions/workflows/jira-process.md +173 -0
- package/content/docs/instructions/workflows/jira-ticket-guide.md +105 -0
- package/content/docs/instructions/workflows/pull-request-generation.md +319 -0
- package/content/docs/instructions/workflows/scrum-process.md +104 -0
- package/content/docs/instructions/workflows/survey-project-setup.md +76 -0
- package/content/docs/knowledge/architecture/README.md +11 -0
- package/content/docs/knowledge/architecture/audio-plugin-architecture.md +213 -0
- package/content/docs/knowledge/architecture/cross-platform-design.md +176 -0
- package/content/docs/knowledge/architecture/frontend-native-bridge.md +193 -0
- package/content/docs/knowledge/architecture/native-command.md +189 -0
- package/content/docs/knowledge/architecture/state-management-architecture.md +105 -0
- package/content/docs/knowledge/component-library/ControlComponent/README.md +281 -0
- package/content/docs/knowledge/component-library/ControlComponent/accessibility/accessibility-implementation.md +503 -0
- package/content/docs/knowledge/component-library/ControlComponent/common-mechanisms.md +278 -0
- package/content/docs/knowledge/component-library/ControlComponent/core/error-handling.md +451 -0
- package/content/docs/knowledge/component-library/ControlComponent/core/native-interface.md +515 -0
- package/content/docs/knowledge/component-library/ControlComponent/core/state-management.md +509 -0
- package/content/docs/knowledge/component-library/ControlComponent/creating-new-controls.md +654 -0
- package/content/docs/knowledge/component-library/ControlComponent/design/api-design-reference.md +1142 -0
- package/content/docs/knowledge/component-library/ControlComponent/design/design-principles.md +336 -0
- package/content/docs/knowledge/component-library/ControlComponent/design/styling-architecture.md +595 -0
- package/content/docs/knowledge/component-library/ControlComponent/design/visual-feedback.md +456 -0
- package/content/docs/knowledge/component-library/ControlComponent/development-environment.md +213 -0
- package/content/docs/knowledge/component-library/ControlComponent/interaction/gesture-algorithms.md +705 -0
- package/content/docs/knowledge/component-library/ControlComponent/interaction/touch-support.md +525 -0
- package/content/docs/knowledge/component-library/ControlComponent/interaction/value-processing-patterns.md +801 -0
- package/content/docs/knowledge/component-library/ControlComponent/interaction/velocity-damping-systems.md +741 -0
- package/content/docs/knowledge/component-library/ControlComponent/knob/architecture.md +490 -0
- package/content/docs/knowledge/component-library/ControlComponent/knob/how-to-use.md +304 -0
- package/content/docs/knowledge/component-library/ControlComponent/knob/index.md +105 -0
- package/content/docs/knowledge/component-library/ControlComponent/optimization/performance-benchmarks.md +535 -0
- package/content/docs/knowledge/component-library/ControlComponent/optimization/performance-optimization.md +1092 -0
- package/content/docs/knowledge/component-library/ControlComponent/quick-start.md +345 -0
- package/content/docs/knowledge/component-library/ControlComponent/slider/architecture.md +444 -0
- package/content/docs/knowledge/component-library/ControlComponent/slider/how-to-use.md +470 -0
- package/content/docs/knowledge/component-library/ControlComponent/slider/index.md +107 -0
- package/content/docs/knowledge/component-library/ControlComponent/testing-guide.md +950 -0
- package/content/docs/knowledge/component-library/ControlComponent/troubleshooting.md +657 -0
- package/content/docs/knowledge/component-library/frontend-develop/LICENSE.txt +176 -0
- package/content/docs/knowledge/component-library/frontend-develop/SKILL.md +124 -0
- package/content/docs/knowledge/component-library/frontend-develop/references/code-organization.md +620 -0
- package/content/docs/knowledge/component-library/frontend-develop/references/coding-standards.md +275 -0
- package/content/docs/knowledge/component-library/frontend-develop/references/component-reusability.md +559 -0
- package/content/docs/knowledge/component-library/frontend-develop/references/examples.md +554 -0
- package/content/docs/knowledge/component-library/frontend-develop/references/layout-separation.md +638 -0
- package/content/docs/knowledge/component-library/frontend-develop/references/performance-optimization.md +678 -0
- package/content/docs/knowledge/component-library/frontend-develop/references/state-management.md +331 -0
- package/content/docs/knowledge/component-library/frontend-develop/references/styling-guidelines.md +349 -0
- package/content/docs/knowledge/component-library/frontend-develop/references/type-safety.md +493 -0
- package/content/docs/knowledge/development/assets/cyberduck-aws-credentials.png +0 -0
- package/content/docs/knowledge/development/assets/postman-environment-setup.png +0 -0
- package/content/docs/knowledge/development/aws-storage.md +95 -0
- package/content/docs/knowledge/development/crm-system.md +22 -0
- package/content/docs/knowledge/development/glossary.md +246 -0
- package/content/docs/knowledge/development/pg-api-guide.md +71 -0
- package/content/docs/knowledge/development/staging-license-management.md +44 -0
- package/content/docs/knowledge/development/tech-stack.md +240 -0
- package/content/docs/knowledge/domain/popup-system.md +106 -0
- package/content/docs/knowledge/domain/sigpath.md +264 -0
- package/content/docs/knowledge/environment-setup/aax-signing-update.md +149 -0
- package/content/docs/knowledge/environment-setup/assets/aax-1.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/aax-2.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/aax-3.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/aax-4.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/aax-5.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/aax-6.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/aax-7.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-1.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-10.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-11.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-12.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-13.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-14.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-2.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-3.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-4.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-5.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-6.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-7.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-8.png +0 -0
- package/content/docs/knowledge/environment-setup/assets/buildmachine-9.png +0 -0
- package/content/docs/knowledge/environment-setup/build-machine-setup.md +224 -0
- package/content/docs/knowledge/environment-setup/build-machine-troubleshooting.md +193 -0
- package/content/docs/knowledge/implementation-guides/adding-amp.md +190 -0
- package/content/docs/knowledge/implementation-guides/adding-fx.md +111 -0
- package/content/docs/knowledge/implementation-guides/cab-integration.md +194 -0
- package/content/docs/knowledge/implementation-guides/custom-pedal-integration.md +309 -0
- package/content/docs/knowledge/projects/BIAS_ONE_GUI/README.md +17 -0
- package/content/manifest.json +122 -0
- package/content/rules/cpp.mdc +135 -0
- package/content/rules/frontend.mdc +615 -0
- package/content/rules/index.mdc +256 -0
- package/content/rules/knowledge.mdc +46 -0
- package/content/rules/team-wide.mdc +179 -0
- package/content/rules/workflows.mdc +43 -0
- package/content/tools/agents/context-compressor.md +357 -0
- package/content/tools/agents/context-writer.md +328 -0
- package/content/tools/agents/release-notes-generator.md +389 -0
- package/content/tools/agents/srs-writer-agent.md +63 -0
- package/content/tools/mcp/README.md +25 -0
- package/content/tools/mcp/mcp-desktop-team.example.json +13 -0
- package/content/tools/skills/frontend-develop/LICENSE.txt +176 -0
- package/content/tools/skills/frontend-develop/SKILL.md +124 -0
- package/content/tools/skills/frontend-develop/references/code-organization.md +620 -0
- package/content/tools/skills/frontend-develop/references/coding-standards.md +275 -0
- package/content/tools/skills/frontend-develop/references/component-reusability.md +559 -0
- package/content/tools/skills/frontend-develop/references/examples.md +554 -0
- package/content/tools/skills/frontend-develop/references/layout-separation.md +638 -0
- package/content/tools/skills/frontend-develop/references/performance-optimization.md +678 -0
- package/content/tools/skills/frontend-develop/references/state-management.md +331 -0
- package/content/tools/skills/frontend-develop/references/styling-guidelines.md +349 -0
- package/content/tools/skills/frontend-develop/references/type-safety.md +493 -0
- package/content/tools/slash-commands/commit.md +17 -0
- package/content/tools/slash-commands/context-compress.md +149 -0
- package/content/tools/slash-commands/context-write.md +92 -0
- package/content/tools/slash-commands/jira.md +12 -0
- package/content/tools/slash-commands/pr-gen.md +12 -0
- package/content/tools/slash-commands/pr-review.md +12 -0
- package/dist/commands/detect.d.ts +1 -0
- package/dist/commands/detect.js +33 -0
- package/dist/commands/install.d.ts +1 -0
- package/dist/commands/install.js +100 -0
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/uninstall.js +132 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +53 -0
- package/dist/lib/detect-env.d.ts +3 -0
- package/dist/lib/detect-env.js +52 -0
- package/dist/lib/prompt-env.d.ts +3 -0
- package/dist/lib/prompt-env.js +16 -0
- package/dist/lib/resolve-doc-repo.d.ts +14 -0
- package/dist/lib/resolve-doc-repo.js +61 -0
- package/dist/lib/symlink.d.ts +7 -0
- package/dist/lib/symlink.js +60 -0
- package/dist/lib/sync-from-manifest.d.ts +8 -0
- package/dist/lib/sync-from-manifest.js +64 -0
- package/package.json +46 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
# Real-World Examples and Best Practices
|
|
2
|
+
|
|
3
|
+
This document provides comprehensive real-world examples demonstrating best practices for frontend development, including component patterns, common anti-patterns, and practical solutions.
|
|
4
|
+
|
|
5
|
+
## Complete Component Example
|
|
6
|
+
|
|
7
|
+
### Example: Reusable Button Component
|
|
8
|
+
|
|
9
|
+
This example demonstrates:
|
|
10
|
+
- Proper TypeScript typing
|
|
11
|
+
- Variant design
|
|
12
|
+
- Performance optimization
|
|
13
|
+
- Style organization
|
|
14
|
+
- Accessibility
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import React, { forwardRef } from 'react';
|
|
18
|
+
import { twMerge } from 'tailwind-merge';
|
|
19
|
+
import clsx from 'clsx';
|
|
20
|
+
|
|
21
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
22
|
+
variant?: 'primary' | 'secondary' | 'outline';
|
|
23
|
+
size?: 'small' | 'medium' | 'large';
|
|
24
|
+
loading?: boolean;
|
|
25
|
+
fullWidth?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
29
|
+
function Button(
|
|
30
|
+
{
|
|
31
|
+
variant = 'primary',
|
|
32
|
+
size = 'medium',
|
|
33
|
+
loading = false,
|
|
34
|
+
fullWidth = false,
|
|
35
|
+
disabled,
|
|
36
|
+
className,
|
|
37
|
+
children,
|
|
38
|
+
...props
|
|
39
|
+
},
|
|
40
|
+
ref
|
|
41
|
+
) {
|
|
42
|
+
const isDisabled = disabled || loading;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<button
|
|
46
|
+
ref={ref}
|
|
47
|
+
type="button"
|
|
48
|
+
disabled={isDisabled}
|
|
49
|
+
className={twMerge(
|
|
50
|
+
'inline-flex items-center justify-center rounded font-medium transition-colors',
|
|
51
|
+
'focus:outline-none focus:ring-2 focus:ring-offset-2',
|
|
52
|
+
variant === 'primary' && 'bg-blue-500 text-white hover:bg-blue-600 focus:ring-blue-500',
|
|
53
|
+
variant === 'secondary' && 'bg-gray-200 text-gray-800 hover:bg-gray-300 focus:ring-gray-500',
|
|
54
|
+
variant === 'outline' && 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50 focus:ring-blue-500',
|
|
55
|
+
size === 'small' && 'px-2 py-1 text-sm',
|
|
56
|
+
size === 'medium' && 'px-4 py-2 text-base',
|
|
57
|
+
size === 'large' && 'px-6 py-3 text-lg',
|
|
58
|
+
{
|
|
59
|
+
'opacity-50 cursor-not-allowed': isDisabled,
|
|
60
|
+
'w-full': fullWidth,
|
|
61
|
+
},
|
|
62
|
+
className
|
|
63
|
+
)}
|
|
64
|
+
aria-busy={loading}
|
|
65
|
+
{...props}
|
|
66
|
+
>
|
|
67
|
+
{loading ? (
|
|
68
|
+
<>
|
|
69
|
+
<Spinner className="mr-2" />
|
|
70
|
+
Loading...
|
|
71
|
+
</>
|
|
72
|
+
) : (
|
|
73
|
+
children
|
|
74
|
+
)}
|
|
75
|
+
</button>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
export default Button;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Common Patterns
|
|
84
|
+
|
|
85
|
+
### Pattern 1: Custom Hook for Data Fetching
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { useState, useEffect } from 'react';
|
|
89
|
+
|
|
90
|
+
interface UseApiDataOptions {
|
|
91
|
+
enabled?: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function useApiData<T>(
|
|
95
|
+
url: string,
|
|
96
|
+
options: UseApiDataOptions = {}
|
|
97
|
+
): {
|
|
98
|
+
data: T | null;
|
|
99
|
+
loading: boolean;
|
|
100
|
+
error: Error | null;
|
|
101
|
+
refetch: () => void;
|
|
102
|
+
} {
|
|
103
|
+
const [data, setData] = useState<T | null>(null);
|
|
104
|
+
const [loading, setLoading] = useState(true);
|
|
105
|
+
const [error, setError] = useState<Error | null>(null);
|
|
106
|
+
const [refetchTrigger, setRefetchTrigger] = useState(0);
|
|
107
|
+
|
|
108
|
+
const refetch = () => {
|
|
109
|
+
setRefetchTrigger(prev => prev + 1);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (options.enabled === false) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let cancelled = false;
|
|
118
|
+
|
|
119
|
+
setLoading(true);
|
|
120
|
+
setError(null);
|
|
121
|
+
|
|
122
|
+
fetch(url)
|
|
123
|
+
.then(res => {
|
|
124
|
+
if (!res.ok) {
|
|
125
|
+
throw new Error(`HTTP error! status: ${res.status}`);
|
|
126
|
+
}
|
|
127
|
+
return res.json();
|
|
128
|
+
})
|
|
129
|
+
.then((data: T) => {
|
|
130
|
+
if (!cancelled) {
|
|
131
|
+
setData(data);
|
|
132
|
+
setLoading(false);
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
.catch((err: Error) => {
|
|
136
|
+
if (!cancelled) {
|
|
137
|
+
setError(err);
|
|
138
|
+
setLoading(false);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return () => {
|
|
143
|
+
cancelled = true;
|
|
144
|
+
};
|
|
145
|
+
}, [url, refetchTrigger, options.enabled]);
|
|
146
|
+
|
|
147
|
+
return { data, loading, error, refetch };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Usage
|
|
151
|
+
interface UserProfileProps {
|
|
152
|
+
userId: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function UserProfile({ userId }: UserProfileProps) {
|
|
156
|
+
const { data: user, loading, error } = useApiData<User>(
|
|
157
|
+
`/api/users/${userId}`
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
if (loading) {
|
|
161
|
+
return <LoadingSpinner />;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (error) {
|
|
165
|
+
return <ErrorMessage error={error} />;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!user) {
|
|
169
|
+
return <EmptyState />;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return <div>{user.name}</div>;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Pattern 2: Form Handling with Validation
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { useState, useCallback } from 'react';
|
|
180
|
+
|
|
181
|
+
interface FormData {
|
|
182
|
+
email: string;
|
|
183
|
+
password: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
interface FormErrors {
|
|
187
|
+
email?: string;
|
|
188
|
+
password?: string;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function validateForm(data: FormData): FormErrors {
|
|
192
|
+
const errors: FormErrors = {};
|
|
193
|
+
|
|
194
|
+
if (!data.email) {
|
|
195
|
+
errors.email = 'Email is required';
|
|
196
|
+
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
|
|
197
|
+
errors.email = 'Invalid email format';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!data.password) {
|
|
201
|
+
errors.password = 'Password is required';
|
|
202
|
+
} else if (data.password.length < 8) {
|
|
203
|
+
errors.password = 'Password must be at least 8 characters';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return errors;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
interface LoginFormProps {
|
|
210
|
+
onSubmit: (data: FormData) => Promise<void>;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function LoginForm({ onSubmit }: LoginFormProps) {
|
|
214
|
+
const [formData, setFormData] = useState<FormData>({
|
|
215
|
+
email: '',
|
|
216
|
+
password: '',
|
|
217
|
+
});
|
|
218
|
+
const [errors, setErrors] = useState<FormErrors>({});
|
|
219
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
220
|
+
|
|
221
|
+
const handleChange = useCallback((field: keyof FormData) => {
|
|
222
|
+
return (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
223
|
+
setFormData(prev => ({ ...prev, [field]: e.target.value }));
|
|
224
|
+
// Clear error when user starts typing
|
|
225
|
+
if (errors[field]) {
|
|
226
|
+
setErrors(prev => ({ ...prev, [field]: undefined }));
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}, [errors]);
|
|
230
|
+
|
|
231
|
+
const handleSubmit = useCallback(async (e: React.FormEvent) => {
|
|
232
|
+
e.preventDefault();
|
|
233
|
+
|
|
234
|
+
const validationErrors = validateForm(formData);
|
|
235
|
+
if (Object.keys(validationErrors).length > 0) {
|
|
236
|
+
setErrors(validationErrors);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
setIsSubmitting(true);
|
|
241
|
+
try {
|
|
242
|
+
await onSubmit(formData);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error('Submit failed:', error);
|
|
245
|
+
} finally {
|
|
246
|
+
setIsSubmitting(false);
|
|
247
|
+
}
|
|
248
|
+
}, [formData, onSubmit]);
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<form onSubmit={handleSubmit}>
|
|
252
|
+
<div>
|
|
253
|
+
<label htmlFor="email">Email</label>
|
|
254
|
+
<input
|
|
255
|
+
id="email"
|
|
256
|
+
type="email"
|
|
257
|
+
value={formData.email}
|
|
258
|
+
onChange={handleChange('email')}
|
|
259
|
+
aria-invalid={!!errors.email}
|
|
260
|
+
aria-describedby={errors.email ? 'email-error' : undefined}
|
|
261
|
+
/>
|
|
262
|
+
{errors.email && (
|
|
263
|
+
<span id="email-error" role="alert">
|
|
264
|
+
{errors.email}
|
|
265
|
+
</span>
|
|
266
|
+
)}
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<div>
|
|
270
|
+
<label htmlFor="password">Password</label>
|
|
271
|
+
<input
|
|
272
|
+
id="password"
|
|
273
|
+
type="password"
|
|
274
|
+
value={formData.password}
|
|
275
|
+
onChange={handleChange('password')}
|
|
276
|
+
aria-invalid={!!errors.password}
|
|
277
|
+
aria-describedby={errors.password ? 'password-error' : undefined}
|
|
278
|
+
/>
|
|
279
|
+
{errors.password && (
|
|
280
|
+
<span id="password-error" role="alert">
|
|
281
|
+
{errors.password}
|
|
282
|
+
</span>
|
|
283
|
+
)}
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
<button type="submit" disabled={isSubmitting}>
|
|
287
|
+
{isSubmitting ? 'Submitting...' : 'Login'}
|
|
288
|
+
</button>
|
|
289
|
+
</form>
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Pattern 3: Optimized List Component
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import React, { memo, useMemo } from 'react';
|
|
298
|
+
|
|
299
|
+
interface Item {
|
|
300
|
+
id: string;
|
|
301
|
+
name: string;
|
|
302
|
+
description: string;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
interface ItemProps {
|
|
306
|
+
item: Item;
|
|
307
|
+
onSelect: (id: string) => void;
|
|
308
|
+
isSelected: boolean;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const Item = memo(function Item({ item, onSelect, isSelected }: ItemProps) {
|
|
312
|
+
const handleClick = React.useCallback(() => {
|
|
313
|
+
onSelect(item.id);
|
|
314
|
+
}, [item.id, onSelect]);
|
|
315
|
+
|
|
316
|
+
return (
|
|
317
|
+
<div
|
|
318
|
+
onClick={handleClick}
|
|
319
|
+
className={clsx(
|
|
320
|
+
'p-4 border rounded cursor-pointer transition',
|
|
321
|
+
{
|
|
322
|
+
'border-blue-500 bg-blue-50': isSelected,
|
|
323
|
+
'border-gray-200 hover:border-gray-300': !isSelected,
|
|
324
|
+
}
|
|
325
|
+
)}
|
|
326
|
+
>
|
|
327
|
+
<h3 className="font-semibold">{item.name}</h3>
|
|
328
|
+
<p className="text-sm text-gray-600">{item.description}</p>
|
|
329
|
+
</div>
|
|
330
|
+
);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
interface ItemListProps {
|
|
334
|
+
items: Item[];
|
|
335
|
+
selectedIds: Set<string>;
|
|
336
|
+
onSelect: (id: string) => void;
|
|
337
|
+
filter?: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function ItemList({ items, selectedIds, onSelect, filter }: ItemListProps) {
|
|
341
|
+
// Memoize filtered items
|
|
342
|
+
const filteredItems = useMemo(() => {
|
|
343
|
+
if (!filter) {
|
|
344
|
+
return items;
|
|
345
|
+
}
|
|
346
|
+
return items.filter(item =>
|
|
347
|
+
item.name.toLowerCase().includes(filter.toLowerCase()) ||
|
|
348
|
+
item.description.toLowerCase().includes(filter.toLowerCase())
|
|
349
|
+
);
|
|
350
|
+
}, [items, filter]);
|
|
351
|
+
|
|
352
|
+
// Memoize callback
|
|
353
|
+
const handleSelect = React.useCallback((id: string) => {
|
|
354
|
+
onSelect(id);
|
|
355
|
+
}, [onSelect]);
|
|
356
|
+
|
|
357
|
+
return (
|
|
358
|
+
<div className="space-y-2">
|
|
359
|
+
{filteredItems.map(item => (
|
|
360
|
+
<Item
|
|
361
|
+
key={item.id}
|
|
362
|
+
item={item}
|
|
363
|
+
onSelect={handleSelect}
|
|
364
|
+
isSelected={selectedIds.has(item.id)}
|
|
365
|
+
/>
|
|
366
|
+
))}
|
|
367
|
+
</div>
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Anti-patterns and Solutions
|
|
373
|
+
|
|
374
|
+
### Anti-pattern 1: Unnecessary useMemo
|
|
375
|
+
|
|
376
|
+
**Bad**:
|
|
377
|
+
```typescript
|
|
378
|
+
function Component({ count }: { count: number }) {
|
|
379
|
+
// Unnecessary: simple calculation
|
|
380
|
+
const doubled = useMemo(() => count * 2, [count]);
|
|
381
|
+
return <div>{doubled}</div>;
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Good**:
|
|
386
|
+
```typescript
|
|
387
|
+
interface ComponentProps {
|
|
388
|
+
count: number;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function Component({ count }: ComponentProps) {
|
|
392
|
+
// Simple calculation, no memoization needed
|
|
393
|
+
const doubled = count * 2;
|
|
394
|
+
return <div>{doubled}</div>;
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Anti-pattern 2: Missing Dependency Arrays
|
|
399
|
+
|
|
400
|
+
**Bad**:
|
|
401
|
+
```typescript
|
|
402
|
+
function Component({ userId }: { userId: string }) {
|
|
403
|
+
const [user, setUser] = useState(null);
|
|
404
|
+
|
|
405
|
+
useEffect(() => {
|
|
406
|
+
fetchUser(userId).then(setUser);
|
|
407
|
+
}, []); // Missing userId dependency
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Good**:
|
|
412
|
+
```typescript
|
|
413
|
+
interface ComponentProps {
|
|
414
|
+
userId: string;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function Component({ userId }: ComponentProps) {
|
|
418
|
+
const [user, setUser] = useState(null);
|
|
419
|
+
|
|
420
|
+
useEffect(() => {
|
|
421
|
+
fetchUser(userId).then(setUser);
|
|
422
|
+
}, [userId]); // Complete dependency array
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Anti-pattern 3: Creating Objects in Render
|
|
427
|
+
|
|
428
|
+
**Bad**:
|
|
429
|
+
```typescript
|
|
430
|
+
interface ParentProps {
|
|
431
|
+
items: Item[];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const MemoizedChild = React.memo(ChildComponent);
|
|
435
|
+
|
|
436
|
+
function Parent({ items }: ParentProps) {
|
|
437
|
+
return (
|
|
438
|
+
<MemoizedChild
|
|
439
|
+
config={{ columns: items[0]?.columns || [] }} // New object every render
|
|
440
|
+
/>
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
**Good**:
|
|
446
|
+
```typescript
|
|
447
|
+
interface ParentProps {
|
|
448
|
+
items: Item[];
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const MemoizedChild = React.memo(ChildComponent);
|
|
452
|
+
|
|
453
|
+
function Parent({ items }: ParentProps) {
|
|
454
|
+
// Memoize config object
|
|
455
|
+
const config = useMemo(() => ({
|
|
456
|
+
columns: items[0]?.columns || [],
|
|
457
|
+
}), [items]);
|
|
458
|
+
|
|
459
|
+
return <MemoizedChild config={config} />;
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Anti-pattern 4: Prop Drilling
|
|
464
|
+
|
|
465
|
+
**Bad**:
|
|
466
|
+
```typescript
|
|
467
|
+
interface HeaderProps {
|
|
468
|
+
user: User | null;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
interface NavProps {
|
|
472
|
+
user: User | null;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function App() {
|
|
476
|
+
const [user, setUser] = useState(null);
|
|
477
|
+
return <Header user={user} />;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function Header({ user }: HeaderProps) {
|
|
481
|
+
return <Nav user={user} />;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function Nav({ user }: NavProps) {
|
|
485
|
+
return <UserMenu user={user} />;
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**Good**:
|
|
490
|
+
```typescript
|
|
491
|
+
// Use context or state management
|
|
492
|
+
const UserContext = createContext<User | null>(null);
|
|
493
|
+
|
|
494
|
+
function App() {
|
|
495
|
+
const [user, setUser] = useState(null);
|
|
496
|
+
return (
|
|
497
|
+
<UserContext.Provider value={user}>
|
|
498
|
+
<Header />
|
|
499
|
+
</UserContext.Provider>
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function Header() {
|
|
504
|
+
return <Nav />;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function Nav() {
|
|
508
|
+
return <UserMenu />;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function UserMenu() {
|
|
512
|
+
const user = useContext(UserContext);
|
|
513
|
+
// Use user
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
## Best Practices Summary
|
|
518
|
+
|
|
519
|
+
### Component Design
|
|
520
|
+
|
|
521
|
+
1. **Single Responsibility**: Each component should have one clear purpose
|
|
522
|
+
2. **Composition over Configuration**: Prefer composition over complex props
|
|
523
|
+
3. **Controlled Components**: Use controlled components for forms
|
|
524
|
+
4. **Accessibility**: Always include ARIA attributes when needed
|
|
525
|
+
|
|
526
|
+
### Performance
|
|
527
|
+
|
|
528
|
+
1. **Measure First**: Use React DevTools Profiler before optimizing
|
|
529
|
+
2. **Memoize Wisely**: Only memoize expensive computations or for reference equality
|
|
530
|
+
3. **Code Split**: Use dynamic imports for route-based splitting
|
|
531
|
+
4. **Avoid Premature Optimization**: Optimize only when there's a measurable problem
|
|
532
|
+
|
|
533
|
+
### State Management
|
|
534
|
+
|
|
535
|
+
1. **Colocate State**: Keep state close to where it's used
|
|
536
|
+
2. **Lift State Up**: Share state by lifting to common ancestor
|
|
537
|
+
3. **Use Redux for Global State**: Use Redux for state shared across many components
|
|
538
|
+
4. **Normalize State**: Normalize nested state structures
|
|
539
|
+
|
|
540
|
+
### Type Safety
|
|
541
|
+
|
|
542
|
+
1. **Avoid `any`**: Use specific types or `unknown`
|
|
543
|
+
2. **Explicit Return Types**: Always define function return types
|
|
544
|
+
3. **Type Guards**: Use type guards for runtime type checking
|
|
545
|
+
4. **Discriminated Unions**: Use for type-safe variant handling
|
|
546
|
+
|
|
547
|
+
## References
|
|
548
|
+
|
|
549
|
+
- [Type Safety Guidelines](./type-safety.md)
|
|
550
|
+
- [Code Organization Guidelines](./code-organization.md)
|
|
551
|
+
- [Performance Optimization](./performance-optimization.md)
|
|
552
|
+
- [State Management Guidelines](./state-management.md)
|
|
553
|
+
- [Styling Guidelines](./styling-guidelines.md)
|
|
554
|
+
- Project Coding Standards: `desktop-team-documentation/instructions/coding-conventions/frontend.md`
|