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,678 @@
|
|
|
1
|
+
# Performance Optimization Guidelines
|
|
2
|
+
|
|
3
|
+
This document provides comprehensive guidelines for optimizing React component performance, including proper use of hooks (`useMemo`, `useCallback`, `useEffect`), re-render optimization, and CSS performance considerations.
|
|
4
|
+
|
|
5
|
+
## Core Principles
|
|
6
|
+
|
|
7
|
+
### Avoid Premature Optimization
|
|
8
|
+
|
|
9
|
+
**Rule**: Optimize only when there's a measurable performance problem.
|
|
10
|
+
|
|
11
|
+
**Process**:
|
|
12
|
+
1. Measure performance first (React DevTools Profiler)
|
|
13
|
+
2. Identify bottlenecks
|
|
14
|
+
3. Apply targeted optimizations
|
|
15
|
+
4. Measure again to verify improvement
|
|
16
|
+
|
|
17
|
+
## useMemo Usage
|
|
18
|
+
|
|
19
|
+
### When to Use useMemo
|
|
20
|
+
|
|
21
|
+
Use `useMemo` when:
|
|
22
|
+
1. **Expensive computations**: Calculations that are computationally expensive
|
|
23
|
+
2. **Reference equality**: When you need to maintain reference equality for dependencies
|
|
24
|
+
3. **Preventing child re-renders**: When passing objects/arrays to memoized children
|
|
25
|
+
|
|
26
|
+
### When NOT to Use useMemo
|
|
27
|
+
|
|
28
|
+
**Avoid** `useMemo` for:
|
|
29
|
+
- Simple calculations (addition, string concatenation)
|
|
30
|
+
- Primitive values (strings, numbers, booleans)
|
|
31
|
+
- Every computation (overhead can outweigh benefits)
|
|
32
|
+
|
|
33
|
+
**Bad**:
|
|
34
|
+
```typescript
|
|
35
|
+
function Component({ count }: { count: number }) {
|
|
36
|
+
// Unnecessary: simple addition is fast
|
|
37
|
+
const doubled = useMemo(() => count * 2, [count]);
|
|
38
|
+
return <div>{doubled}</div>;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Good**:
|
|
43
|
+
```typescript
|
|
44
|
+
interface ComponentProps {
|
|
45
|
+
count: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Component({ count }: ComponentProps) {
|
|
49
|
+
// Simple calculation, no memoization needed
|
|
50
|
+
const doubled = count * 2;
|
|
51
|
+
return <div>{doubled}</div>;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### useMemo Best Practices
|
|
56
|
+
|
|
57
|
+
**Example 1: Expensive Computation**
|
|
58
|
+
```typescript
|
|
59
|
+
interface DataTableProps {
|
|
60
|
+
data: DataItem[];
|
|
61
|
+
filters: FilterConfig;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function DataTable({ data, filters }: DataTableProps) {
|
|
65
|
+
// Expensive: filtering and sorting large dataset
|
|
66
|
+
const filteredData = useMemo(() => {
|
|
67
|
+
return data
|
|
68
|
+
.filter(item => matchesFilters(item, filters))
|
|
69
|
+
.sort((a, b) => a.date - b.date);
|
|
70
|
+
}, [data, filters]);
|
|
71
|
+
|
|
72
|
+
return <Table data={filteredData} />;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Example 2: Reference Equality for Dependencies**
|
|
77
|
+
```typescript
|
|
78
|
+
interface ComponentProps {
|
|
79
|
+
items: Item[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function Component({ items }: ComponentProps) {
|
|
83
|
+
// Maintain reference equality to prevent unnecessary re-renders
|
|
84
|
+
const itemMap = useMemo(() => {
|
|
85
|
+
return new Map(items.map(item => [item.id, item]));
|
|
86
|
+
}, [items]);
|
|
87
|
+
|
|
88
|
+
return <ChildComponent itemMap={itemMap} />;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Example 3: Preventing Child Re-renders**
|
|
93
|
+
```typescript
|
|
94
|
+
interface ParentProps {
|
|
95
|
+
data: Data[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const MemoizedChild = React.memo(ChildComponent);
|
|
99
|
+
|
|
100
|
+
function Parent({ data }: ParentProps) {
|
|
101
|
+
// Memoize object to prevent Child re-renders
|
|
102
|
+
const config = useMemo(() => ({
|
|
103
|
+
columns: data[0]?.columns || [],
|
|
104
|
+
sortable: true,
|
|
105
|
+
}), [data]);
|
|
106
|
+
|
|
107
|
+
return <MemoizedChild config={config} />;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## useCallback Usage
|
|
112
|
+
|
|
113
|
+
### When to Use useCallback
|
|
114
|
+
|
|
115
|
+
Use `useCallback` when:
|
|
116
|
+
1. **Memoized children**: Passing functions to `React.memo` components
|
|
117
|
+
2. **Hook dependencies**: Functions used in `useEffect`, `useMemo`, or other hooks
|
|
118
|
+
3. **Expensive function creation**: Functions that are expensive to create
|
|
119
|
+
|
|
120
|
+
### When NOT to Use useCallback
|
|
121
|
+
|
|
122
|
+
**Avoid** `useCallback` for:
|
|
123
|
+
- Functions passed to native DOM elements
|
|
124
|
+
- Functions that change on every render anyway
|
|
125
|
+
- Simple inline functions
|
|
126
|
+
|
|
127
|
+
**Bad**:
|
|
128
|
+
```typescript
|
|
129
|
+
function Component({ onClick }: { onClick: () => void }) {
|
|
130
|
+
// Unnecessary: onClick is already stable
|
|
131
|
+
const handleClick = useCallback(() => {
|
|
132
|
+
onClick();
|
|
133
|
+
}, [onClick]);
|
|
134
|
+
|
|
135
|
+
return <button onClick={handleClick}>Click</button>;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Good**:
|
|
140
|
+
```typescript
|
|
141
|
+
interface ComponentProps {
|
|
142
|
+
onClick: () => void;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function Component({ onClick }: ComponentProps) {
|
|
146
|
+
// Direct pass-through is fine
|
|
147
|
+
return <button onClick={onClick}>Click</button>;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### useCallback Best Practices
|
|
152
|
+
|
|
153
|
+
**Example 1: Memoized Child Component**
|
|
154
|
+
```typescript
|
|
155
|
+
interface ParentProps {
|
|
156
|
+
items: Item[];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const MemoizedChild = React.memo(ChildComponent);
|
|
160
|
+
|
|
161
|
+
function Parent({ items }: ParentProps) {
|
|
162
|
+
// Memoize callback to prevent Child re-renders
|
|
163
|
+
const handleItemClick = useCallback((id: string) => {
|
|
164
|
+
console.log('Item clicked:', id);
|
|
165
|
+
}, []);
|
|
166
|
+
|
|
167
|
+
return <MemoizedChild items={items} onItemClick={handleItemClick} />;
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Example 2: Hook Dependencies**
|
|
172
|
+
```typescript
|
|
173
|
+
interface ComponentProps {
|
|
174
|
+
userId: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function Component({ userId }: ComponentProps) {
|
|
178
|
+
// Memoize function used in useEffect
|
|
179
|
+
const fetchUser = useCallback(async () => {
|
|
180
|
+
const response = await fetch(`/api/users/${userId}`);
|
|
181
|
+
return response.json();
|
|
182
|
+
}, [userId]);
|
|
183
|
+
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
fetchUser().then(setUser);
|
|
186
|
+
}, [fetchUser]);
|
|
187
|
+
|
|
188
|
+
return <div>{/* ... */}</div>;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Example 3: Event Handler with Dependencies**
|
|
193
|
+
```typescript
|
|
194
|
+
interface ComponentProps {
|
|
195
|
+
items: Item[];
|
|
196
|
+
onUpdate: (item: Item) => void;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function Component({ items, onUpdate }: ComponentProps) {
|
|
200
|
+
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
201
|
+
|
|
202
|
+
// Memoize handler that depends on state
|
|
203
|
+
const handleSelect = useCallback((id: string) => {
|
|
204
|
+
setSelectedId(id);
|
|
205
|
+
const item = items.find(i => i.id === id);
|
|
206
|
+
if (item) {
|
|
207
|
+
onUpdate(item);
|
|
208
|
+
}
|
|
209
|
+
}, [items, onUpdate]);
|
|
210
|
+
|
|
211
|
+
return <ItemList items={items} onSelect={handleSelect} />;
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## useEffect Best Practices
|
|
216
|
+
|
|
217
|
+
### Dependency Array Rules
|
|
218
|
+
|
|
219
|
+
**Rule**: Always specify complete dependency arrays.
|
|
220
|
+
|
|
221
|
+
**Bad**:
|
|
222
|
+
```typescript
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
fetchData(userId, filters);
|
|
225
|
+
}, [userId]); // Missing filters dependency
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Good**:
|
|
229
|
+
```typescript
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
fetchData(userId, filters);
|
|
232
|
+
}, [userId, filters]); // Complete dependency array
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Avoiding Unnecessary Effects
|
|
236
|
+
|
|
237
|
+
**Rule**: Don't use `useEffect` for derived state.
|
|
238
|
+
|
|
239
|
+
**Bad**:
|
|
240
|
+
```typescript
|
|
241
|
+
function Component({ items }: { items: Item[] }) {
|
|
242
|
+
const [filteredItems, setFilteredItems] = useState<Item[]>([]);
|
|
243
|
+
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
setFilteredItems(items.filter(item => item.active));
|
|
246
|
+
}, [items]);
|
|
247
|
+
|
|
248
|
+
return <ItemList items={filteredItems} />;
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Good**:
|
|
253
|
+
```typescript
|
|
254
|
+
function Component({ items }: { items: Item[] }) {
|
|
255
|
+
// Compute directly, no effect needed
|
|
256
|
+
const filteredItems = items.filter(item => item.active);
|
|
257
|
+
|
|
258
|
+
return <ItemList items={filteredItems} />;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Effect Cleanup
|
|
263
|
+
|
|
264
|
+
**Rule**: Always clean up subscriptions, timers, and event listeners.
|
|
265
|
+
|
|
266
|
+
**Example**:
|
|
267
|
+
```typescript
|
|
268
|
+
function Component({ userId }: { userId: string }) {
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
const controller = new AbortController();
|
|
271
|
+
|
|
272
|
+
fetch(`/api/users/${userId}`, {
|
|
273
|
+
signal: controller.signal,
|
|
274
|
+
})
|
|
275
|
+
.then(res => res.json())
|
|
276
|
+
.then(setUser)
|
|
277
|
+
.catch(err => {
|
|
278
|
+
if (err.name !== 'AbortError') {
|
|
279
|
+
console.error(err);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Cleanup: abort request on unmount or userId change
|
|
284
|
+
return () => {
|
|
285
|
+
controller.abort();
|
|
286
|
+
};
|
|
287
|
+
}, [userId]);
|
|
288
|
+
|
|
289
|
+
return <div>{/* ... */}</div>;
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Effect Optimization Patterns
|
|
294
|
+
|
|
295
|
+
**Pattern 1: Debounced Effects**
|
|
296
|
+
```typescript
|
|
297
|
+
interface SearchInputProps {
|
|
298
|
+
onSearch: (query: string) => void;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function SearchInput({ onSearch }: SearchInputProps) {
|
|
302
|
+
const [query, setQuery] = useState('');
|
|
303
|
+
|
|
304
|
+
useEffect(() => {
|
|
305
|
+
const timer = setTimeout(() => {
|
|
306
|
+
onSearch(query);
|
|
307
|
+
}, 300);
|
|
308
|
+
|
|
309
|
+
return () => clearTimeout(timer);
|
|
310
|
+
}, [query, onSearch]);
|
|
311
|
+
|
|
312
|
+
return <input value={query} onChange={e => setQuery(e.target.value)} />;
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Pattern 2: Conditional Effects**
|
|
317
|
+
```typescript
|
|
318
|
+
interface ComponentProps {
|
|
319
|
+
enabled: boolean;
|
|
320
|
+
userId: string;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function Component({ enabled, userId }: ComponentProps) {
|
|
324
|
+
useEffect(() => {
|
|
325
|
+
if (!enabled) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Effect logic only runs when enabled is true
|
|
330
|
+
const subscription = subscribe(userId);
|
|
331
|
+
return () => subscription.unsubscribe();
|
|
332
|
+
}, [enabled, userId]);
|
|
333
|
+
|
|
334
|
+
return <div>{/* ... */}</div>;
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Re-render Optimization
|
|
339
|
+
|
|
340
|
+
### Identifying Re-render Issues
|
|
341
|
+
|
|
342
|
+
**Tools**:
|
|
343
|
+
- React DevTools Profiler
|
|
344
|
+
- `why-did-you-render` library
|
|
345
|
+
- Console logs with `useEffect`
|
|
346
|
+
|
|
347
|
+
### Common Causes of Unnecessary Re-renders
|
|
348
|
+
|
|
349
|
+
1. **New object/array references**: Creating new objects/arrays in render
|
|
350
|
+
2. **Inline functions**: Creating functions in render
|
|
351
|
+
3. **State updates**: Updating state that doesn't affect render
|
|
352
|
+
4. **Parent re-renders**: Parent component re-rendering causes child re-renders
|
|
353
|
+
|
|
354
|
+
### Optimization Strategies
|
|
355
|
+
|
|
356
|
+
#### Strategy 1: React.memo
|
|
357
|
+
|
|
358
|
+
**Rule**: Use `React.memo` for expensive components that receive stable props.
|
|
359
|
+
|
|
360
|
+
**Example**:
|
|
361
|
+
```typescript
|
|
362
|
+
interface ItemProps {
|
|
363
|
+
id: string;
|
|
364
|
+
name: string;
|
|
365
|
+
onClick: (id: string) => void;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const Item = React.memo(function Item({ id, name, onClick }: ItemProps) {
|
|
369
|
+
return (
|
|
370
|
+
<div onClick={() => onClick(id)}>
|
|
371
|
+
{name}
|
|
372
|
+
</div>
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
function ItemList({ items, onItemClick }: Props) {
|
|
377
|
+
return (
|
|
378
|
+
<div>
|
|
379
|
+
{items.map(item => (
|
|
380
|
+
<Item
|
|
381
|
+
key={item.id}
|
|
382
|
+
id={item.id}
|
|
383
|
+
name={item.name}
|
|
384
|
+
onClick={onItemClick}
|
|
385
|
+
/>
|
|
386
|
+
))}
|
|
387
|
+
</div>
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
#### Strategy 2: Memoize Props
|
|
393
|
+
|
|
394
|
+
**Rule**: Memoize objects/arrays passed to memoized children.
|
|
395
|
+
|
|
396
|
+
**Example**:
|
|
397
|
+
```typescript
|
|
398
|
+
interface ParentProps {
|
|
399
|
+
data: Data[];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const MemoizedChild = React.memo(ChildComponent);
|
|
403
|
+
|
|
404
|
+
function Parent({ data }: ParentProps) {
|
|
405
|
+
// Memoize config object
|
|
406
|
+
const config = useMemo(() => ({
|
|
407
|
+
columns: data[0]?.columns || [],
|
|
408
|
+
sortable: true,
|
|
409
|
+
}), [data]);
|
|
410
|
+
|
|
411
|
+
// Memoize callback
|
|
412
|
+
const handleChange = useCallback((value: string) => {
|
|
413
|
+
console.log(value);
|
|
414
|
+
}, []);
|
|
415
|
+
|
|
416
|
+
return <MemoizedChild config={config} onChange={handleChange} />;
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
#### Strategy 3: Split Components
|
|
421
|
+
|
|
422
|
+
**Rule**: Split components to isolate re-renders.
|
|
423
|
+
|
|
424
|
+
**Example**:
|
|
425
|
+
```typescript
|
|
426
|
+
// Bad: Entire component re-renders when count changes
|
|
427
|
+
function Component({ items, count }: Props) {
|
|
428
|
+
return (
|
|
429
|
+
<div>
|
|
430
|
+
<ExpensiveList items={items} />
|
|
431
|
+
<Counter count={count} />
|
|
432
|
+
</div>
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Good: Split to isolate re-renders
|
|
437
|
+
function Component({ items, count }: Props) {
|
|
438
|
+
return (
|
|
439
|
+
<div>
|
|
440
|
+
<ItemsList items={items} />
|
|
441
|
+
<Counter count={count} />
|
|
442
|
+
</div>
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
interface ItemsListProps {
|
|
447
|
+
items: Item[];
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const ItemsList = React.memo(function ItemsList({ items }: ItemsListProps) {
|
|
451
|
+
return <ExpensiveList items={items} />;
|
|
452
|
+
});
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
#### Strategy 4: State Colocation
|
|
456
|
+
|
|
457
|
+
**Rule**: Keep state as close to where it's used as possible.
|
|
458
|
+
|
|
459
|
+
**Example**:
|
|
460
|
+
```typescript
|
|
461
|
+
// Bad: State in parent causes all children to re-render
|
|
462
|
+
function Parent() {
|
|
463
|
+
const [count, setCount] = useState(0);
|
|
464
|
+
return (
|
|
465
|
+
<div>
|
|
466
|
+
<ExpensiveChild1 />
|
|
467
|
+
<ExpensiveChild2 />
|
|
468
|
+
<Counter count={count} onIncrement={() => setCount(c => c + 1)} />
|
|
469
|
+
</div>
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Good: State colocated with component that uses it
|
|
474
|
+
function Parent() {
|
|
475
|
+
return (
|
|
476
|
+
<div>
|
|
477
|
+
<ExpensiveChild1 />
|
|
478
|
+
<ExpensiveChild2 />
|
|
479
|
+
<Counter />
|
|
480
|
+
</div>
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function Counter() {
|
|
485
|
+
const [count, setCount] = useState(0);
|
|
486
|
+
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## CSS Performance Optimization
|
|
491
|
+
|
|
492
|
+
### Browser Rendering Optimization
|
|
493
|
+
|
|
494
|
+
**Rule**: Optimize CSS to minimize layout thrashing and repaints.
|
|
495
|
+
|
|
496
|
+
#### Avoid Layout Thrashing
|
|
497
|
+
|
|
498
|
+
**Bad**:
|
|
499
|
+
```typescript
|
|
500
|
+
// Forces layout recalculation
|
|
501
|
+
function Component() {
|
|
502
|
+
const [width, setWidth] = useState(0);
|
|
503
|
+
|
|
504
|
+
useEffect(() => {
|
|
505
|
+
const element = ref.current;
|
|
506
|
+
if (element) {
|
|
507
|
+
const newWidth = element.offsetWidth; // Read: forces layout
|
|
508
|
+
setWidth(newWidth);
|
|
509
|
+
element.style.width = `${newWidth * 2}px`; // Write: forces layout
|
|
510
|
+
}
|
|
511
|
+
}, []);
|
|
512
|
+
|
|
513
|
+
return <div ref={ref}>Content</div>;
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**Good**:
|
|
518
|
+
```typescript
|
|
519
|
+
// Batch reads and writes
|
|
520
|
+
function Component() {
|
|
521
|
+
const [width, setWidth] = useState(0);
|
|
522
|
+
|
|
523
|
+
useEffect(() => {
|
|
524
|
+
const element = ref.current;
|
|
525
|
+
if (element) {
|
|
526
|
+
// Batch all reads first
|
|
527
|
+
const newWidth = element.offsetWidth;
|
|
528
|
+
|
|
529
|
+
// Then batch all writes
|
|
530
|
+
requestAnimationFrame(() => {
|
|
531
|
+
element.style.width = `${newWidth * 2}px`;
|
|
532
|
+
setWidth(newWidth);
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
}, []);
|
|
536
|
+
|
|
537
|
+
return <div ref={ref}>Content</div>;
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
#### Use CSS Transforms Instead of Position Changes
|
|
542
|
+
|
|
543
|
+
**Rule**: Prefer `transform` over `top`/`left` for animations.
|
|
544
|
+
|
|
545
|
+
**Bad**:
|
|
546
|
+
```css
|
|
547
|
+
.animated {
|
|
548
|
+
position: absolute;
|
|
549
|
+
top: 0;
|
|
550
|
+
left: 0;
|
|
551
|
+
transition: top 0.3s, left 0.3s;
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
**Good**:
|
|
556
|
+
```css
|
|
557
|
+
.animated {
|
|
558
|
+
position: absolute;
|
|
559
|
+
top: 0;
|
|
560
|
+
left: 0;
|
|
561
|
+
transform: translate(0, 0);
|
|
562
|
+
transition: transform 0.3s;
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
#### Avoid Expensive CSS Properties
|
|
567
|
+
|
|
568
|
+
**Rule**: Avoid properties that trigger layout recalculation.
|
|
569
|
+
|
|
570
|
+
**Expensive Properties** (trigger layout):
|
|
571
|
+
- `width`, `height`
|
|
572
|
+
- `top`, `left`, `right`, `bottom`
|
|
573
|
+
- `margin`, `padding`
|
|
574
|
+
- `border-width`
|
|
575
|
+
|
|
576
|
+
**Cheap Properties** (only trigger paint/composite):
|
|
577
|
+
- `transform`
|
|
578
|
+
- `opacity`
|
|
579
|
+
- `filter`
|
|
580
|
+
- `will-change`
|
|
581
|
+
|
|
582
|
+
#### Use will-change Sparingly
|
|
583
|
+
|
|
584
|
+
**Rule**: Use `will-change` only for elements that will animate.
|
|
585
|
+
|
|
586
|
+
**Example**:
|
|
587
|
+
```css
|
|
588
|
+
.animated-element {
|
|
589
|
+
will-change: transform;
|
|
590
|
+
transition: transform 0.3s;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/* Remove will-change after animation */
|
|
594
|
+
.animated-element.animation-complete {
|
|
595
|
+
will-change: auto;
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### TailwindCSS Performance
|
|
600
|
+
|
|
601
|
+
**Rule**: Use Tailwind utilities efficiently.
|
|
602
|
+
|
|
603
|
+
**Best Practices**:
|
|
604
|
+
- Use utility classes instead of custom CSS when possible
|
|
605
|
+
- Avoid deeply nested selectors
|
|
606
|
+
- Use `twMerge` to merge conflicting classes efficiently
|
|
607
|
+
|
|
608
|
+
**Example**:
|
|
609
|
+
```typescript
|
|
610
|
+
import { twMerge } from 'tailwind-merge';
|
|
611
|
+
|
|
612
|
+
function Component({ className, variant }: Props) {
|
|
613
|
+
return (
|
|
614
|
+
<div
|
|
615
|
+
className={twMerge(
|
|
616
|
+
'p-4 rounded-lg',
|
|
617
|
+
variant === 'primary' && 'bg-blue-500 text-white',
|
|
618
|
+
variant === 'secondary' && 'bg-gray-200 text-gray-800',
|
|
619
|
+
className
|
|
620
|
+
)}
|
|
621
|
+
>
|
|
622
|
+
Content
|
|
623
|
+
</div>
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
## Performance Measurement
|
|
629
|
+
|
|
630
|
+
### React DevTools Profiler
|
|
631
|
+
|
|
632
|
+
**Usage**:
|
|
633
|
+
1. Open React DevTools
|
|
634
|
+
2. Go to Profiler tab
|
|
635
|
+
3. Click Record
|
|
636
|
+
4. Interact with application
|
|
637
|
+
5. Stop recording
|
|
638
|
+
6. Analyze flamegraph and ranked chart
|
|
639
|
+
|
|
640
|
+
### Performance Metrics
|
|
641
|
+
|
|
642
|
+
**Key Metrics**:
|
|
643
|
+
- **Render time**: Time spent rendering component
|
|
644
|
+
- **Commit time**: Time spent committing changes to DOM
|
|
645
|
+
- **Re-render frequency**: How often component re-renders
|
|
646
|
+
|
|
647
|
+
### Optimization Checklist
|
|
648
|
+
|
|
649
|
+
When optimizing performance:
|
|
650
|
+
|
|
651
|
+
- [ ] Measured performance with React DevTools Profiler
|
|
652
|
+
- [ ] Identified specific bottlenecks
|
|
653
|
+
- [ ] Applied targeted optimizations
|
|
654
|
+
- [ ] Verified improvements with measurements
|
|
655
|
+
- [ ] `useMemo` used only for expensive computations
|
|
656
|
+
- [ ] `useCallback` used only for memoized children or hook dependencies
|
|
657
|
+
- [ ] `useEffect` dependencies complete and correct
|
|
658
|
+
- [ ] Unnecessary re-renders eliminated
|
|
659
|
+
- [ ] CSS optimized to avoid layout thrashing
|
|
660
|
+
- [ ] Transform/opacity used for animations instead of position changes
|
|
661
|
+
|
|
662
|
+
## Summary Checklist
|
|
663
|
+
|
|
664
|
+
When optimizing performance, ensure:
|
|
665
|
+
|
|
666
|
+
- [ ] Performance measured before optimization
|
|
667
|
+
- [ ] `useMemo` used only when necessary (expensive computations)
|
|
668
|
+
- [ ] `useCallback` used only when necessary (memoized children, hook deps)
|
|
669
|
+
- [ ] `useEffect` dependencies complete
|
|
670
|
+
- [ ] Re-renders optimized (React.memo, state colocation)
|
|
671
|
+
- [ ] CSS optimized (avoid layout thrashing, use transforms)
|
|
672
|
+
- [ ] Improvements verified with measurements
|
|
673
|
+
|
|
674
|
+
## References
|
|
675
|
+
|
|
676
|
+
- [React Performance Optimization](https://react.dev/learn/render-and-commit)
|
|
677
|
+
- [Web.dev Performance](https://web.dev/performance/)
|
|
678
|
+
- Project Coding Standards: `desktop-team-documentation/instructions/coding-conventions/frontend.md`
|