@kurokeita/add-skill 2.6.2 → 3.0.1
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 +43 -64
- package/dist/bin/cli.js +270 -478
- package/dist/skills/statusline-setup/.claude-plugin/plugin.json +1 -1
- package/dist/skills/statusline-setup/SKILL.md +21 -436
- package/dist/skills/statusline-setup/mod/hooks/register.tsx +93 -103
- package/dist/skills/statusline-setup/mod/tests/work.test.ts +28 -10
- package/dist/skills/statusline-setup/mod/types/index.d.ts +2 -1
- package/dist/skills/universalize-agents/reference/mapping.md +3 -2
- package/package.json +3 -3
- package/dist/agents/backend-specialist.md +0 -277
- package/dist/agents/code-archaeologist.md +0 -114
- package/dist/agents/database-architect.md +0 -238
- package/dist/agents/debugger.md +0 -231
- package/dist/agents/devops-engineer.md +0 -242
- package/dist/agents/documentation-writer.md +0 -104
- package/dist/agents/explorer-agent.md +0 -79
- package/dist/agents/frontend-specialist.md +0 -594
- package/dist/agents/game-developer.md +0 -162
- package/dist/agents/mobile-developer.md +0 -383
- package/dist/agents/orchestrator.md +0 -435
- package/dist/agents/penetration-tester.md +0 -188
- package/dist/agents/performance-optimizer.md +0 -191
- package/dist/agents/product-manager.md +0 -122
- package/dist/agents/product-owner.md +0 -106
- package/dist/agents/project-planner.md +0 -408
- package/dist/agents/qa-automation-engineer.md +0 -109
- package/dist/agents/security-auditor.md +0 -171
- package/dist/agents/test-engineer.md +0 -158
- package/dist/skills/agent-identifier/SKILL.md +0 -431
- package/dist/skills/agent-identifier/examples/agent-creation-prompt.md +0 -247
- package/dist/skills/agent-identifier/examples/complete-agent-examples.md +0 -432
- package/dist/skills/agent-identifier/references/agent-creation-system-prompt.md +0 -215
- package/dist/skills/agent-identifier/references/system-prompt-design.md +0 -419
- package/dist/skills/agent-identifier/references/triggering-examples.md +0 -508
- package/dist/skills/agent-identifier/scripts/validate-agent.sh +0 -217
- package/dist/skills/code-review-excellence/SKILL.md +0 -525
- package/dist/skills/component-refactoring/SKILL.md +0 -492
- package/dist/skills/component-refactoring/references/complexity-patterns.md +0 -493
- package/dist/skills/component-refactoring/references/component-splitting.md +0 -477
- package/dist/skills/component-refactoring/references/hook-extraction.md +0 -317
- package/dist/skills/context7-guidelines/SKILL.md +0 -31
- package/dist/skills/feature-design-assistant/SKILL.md +0 -371
- package/dist/skills/find-bugs/SKILL.md +0 -75
- package/dist/skills/frontend-design/SKILL.md +0 -45
- package/dist/skills/go-concurrency-patterns/SKILL.md +0 -657
- package/dist/skills/grill-me/SKILL.md +0 -9
- package/dist/skills/handoff/SKILL.md +0 -16
- package/dist/skills/koreader-plugin-development/SKILL.md +0 -141
- package/dist/skills/koreader-plugin-development/examples/2-example-patch.lua +0 -17
- package/dist/skills/koreader-plugin-development/examples/_meta.lua +0 -6
- package/dist/skills/koreader-plugin-development/examples/helloworld.lua +0 -36
- package/dist/skills/koreader-plugin-development/references/debugging.md +0 -92
- package/dist/skills/koreader-plugin-development/references/events-and-widgets.md +0 -102
- package/dist/skills/koreader-plugin-development/references/patches.md +0 -118
- package/dist/skills/koreader-plugin-development/references/plugin-anatomy.md +0 -97
- package/dist/skills/make-instruction/SKILL.md +0 -68
- package/dist/skills/make-prompt/SKILL.md +0 -70
- package/dist/skills/make-skill/SKILL.md +0 -147
- package/dist/skills/prd-to-tasks/SKILL.md +0 -164
- package/dist/skills/react-native-async-patterns/SKILL.md +0 -256
- package/dist/skills/react-native-performance/SKILL.md +0 -425
- package/dist/skills/react-native-testing/SKILL.md +0 -458
- package/dist/skills/react-native-zustand-patterns/SKILL.md +0 -343
- package/dist/skills/skill-creator/SKILL.md +0 -385
- package/dist/skills/skill-creator/scripts/init_skill.cjs +0 -235
- package/dist/skills/skill-creator/scripts/package_skill.cjs +0 -84
- package/dist/skills/skill-creator/scripts/validate_skill.cjs +0 -127
- package/dist/skills/skill-development/SKILL.md +0 -676
- package/dist/skills/skill-development/references/skill-creator-original.md +0 -210
- package/dist/skills/sql-optimization-patterns/SKILL.md +0 -509
- package/dist/skills/test-driven-development/SKILL.md +0 -114
- package/dist/skills/test-driven-development/deep-modules.md +0 -33
- package/dist/skills/test-driven-development/interface-design.md +0 -35
- package/dist/skills/test-driven-development/mocking.md +0 -68
- package/dist/skills/test-driven-development/refactoring.md +0 -10
- package/dist/skills/test-driven-development/tests.md +0 -61
- package/dist/skills/vercel-react-best-practices/AGENTS.md +0 -2410
- package/dist/skills/vercel-react-best-practices/SKILL.md +0 -127
- package/dist/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
- package/dist/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -49
- package/dist/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
- package/dist/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
- package/dist/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -36
- package/dist/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
- package/dist/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
- package/dist/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
- package/dist/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
- package/dist/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
- package/dist/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
- package/dist/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
- package/dist/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
- package/dist/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
- package/dist/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
- package/dist/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
- package/dist/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -82
- package/dist/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
- package/dist/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
- package/dist/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
- package/dist/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
- package/dist/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
- package/dist/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/dist/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
- package/dist/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -50
- package/dist/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
- package/dist/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/dist/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/dist/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
- package/dist/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/dist/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/dist/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/dist/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
- package/dist/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
- package/dist/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/dist/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/dist/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
- package/dist/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
- package/dist/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/dist/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/dist/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
- package/dist/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
- package/dist/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
- package/dist/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
- package/dist/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
- package/dist/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
- package/dist/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
- package/dist/skills/web-design-guidelines/SKILL.md +0 -40
- package/dist/skills/workflow-patterns/SKILL.md +0 -623
- package/dist/skills/write-prd/SKILL.md +0 -162
- package/dist/workflows/brainstorm.md +0 -113
- package/dist/workflows/create.md +0 -60
- package/dist/workflows/debug.md +0 -107
- package/dist/workflows/deploy.md +0 -178
- package/dist/workflows/enhance.md +0 -64
- package/dist/workflows/github-pull-request-create.md +0 -134
- package/dist/workflows/orchestrate.md +0 -246
- package/dist/workflows/plan.md +0 -90
- package/dist/workflows/preview.md +0 -83
- package/dist/workflows/status.md +0 -87
- package/dist/workflows/test.md +0 -144
- package/dist/workflows/ui-ux-pro-max.md +0 -315
|
@@ -1,425 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: react-native-performance
|
|
3
|
-
description: Performance optimization for React Native. Use when optimizing lists, preventing re-renders, memoizing components, or debugging performance issues in Expo/React Native apps.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# React Native Performance
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
React Native performance issues often stem from unnecessary re-renders, unoptimized lists, and expensive computations on the JS thread. This codebase has performance-critical areas (shot mastery, player lists) with established optimization patterns.
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Pattern: FlatList Optimization
|
|
15
|
-
|
|
16
|
-
### keyExtractor - Stable Keys
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
// ✅ CORRECT: Stable function reference
|
|
20
|
-
const keyExtractor = useCallback((item: Session) => item.id, []);
|
|
21
|
-
|
|
22
|
-
<FlatList
|
|
23
|
-
data={sessions}
|
|
24
|
-
keyExtractor={keyExtractor}
|
|
25
|
-
renderItem={renderItem}
|
|
26
|
-
/>
|
|
27
|
-
|
|
28
|
-
// ❌ WRONG: Creates new function every render
|
|
29
|
-
<FlatList
|
|
30
|
-
data={sessions}
|
|
31
|
-
keyExtractor={(item) => item.id}
|
|
32
|
-
renderItem={renderItem}
|
|
33
|
-
/>
|
|
34
|
-
|
|
35
|
-
// ❌ WRONG: Using index (causes issues with reordering/deletion)
|
|
36
|
-
keyExtractor={(item, index) => `${index}`}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### getItemLayout - Fixed Height Items
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
const ITEM_HEIGHT = 80;
|
|
43
|
-
const SEPARATOR_HEIGHT = 1;
|
|
44
|
-
|
|
45
|
-
const getItemLayout = useCallback(
|
|
46
|
-
(data: Session[] | null | undefined, index: number) => ({
|
|
47
|
-
length: ITEM_HEIGHT,
|
|
48
|
-
offset: (ITEM_HEIGHT + SEPARATOR_HEIGHT) * index,
|
|
49
|
-
index,
|
|
50
|
-
}),
|
|
51
|
-
[]
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
<FlatList
|
|
55
|
-
data={sessions}
|
|
56
|
-
getItemLayout={getItemLayout}
|
|
57
|
-
// ... other props
|
|
58
|
-
/>
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
**Why it matters:** Without `getItemLayout`, FlatList must measure each item, causing scroll jank.
|
|
62
|
-
|
|
63
|
-
### renderItem - Memoized
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
// Extract to named component
|
|
67
|
-
const SessionItem = memo(function SessionItem({
|
|
68
|
-
session,
|
|
69
|
-
onPress
|
|
70
|
-
}: {
|
|
71
|
-
session: Session;
|
|
72
|
-
onPress: (id: string) => void;
|
|
73
|
-
}) {
|
|
74
|
-
return (
|
|
75
|
-
<Pressable onPress={() => onPress(session.id)}>
|
|
76
|
-
<Text>{session.title}</Text>
|
|
77
|
-
</Pressable>
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// Stable callback
|
|
82
|
-
const handlePress = useCallback((id: string) => {
|
|
83
|
-
navigation.push(`/session/${id}`);
|
|
84
|
-
}, [navigation]);
|
|
85
|
-
|
|
86
|
-
// Stable renderItem
|
|
87
|
-
const renderItem = useCallback(
|
|
88
|
-
({ item }: { item: Session }) => (
|
|
89
|
-
<SessionItem session={item} onPress={handlePress} />
|
|
90
|
-
),
|
|
91
|
-
[handlePress]
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
<FlatList
|
|
95
|
-
data={sessions}
|
|
96
|
-
renderItem={renderItem}
|
|
97
|
-
// ...
|
|
98
|
-
/>
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Additional Optimizations
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
<FlatList
|
|
105
|
-
data={sessions}
|
|
106
|
-
renderItem={renderItem}
|
|
107
|
-
keyExtractor={keyExtractor}
|
|
108
|
-
getItemLayout={getItemLayout}
|
|
109
|
-
|
|
110
|
-
// Performance props
|
|
111
|
-
removeClippedSubviews={true} // Unmount off-screen items
|
|
112
|
-
maxToRenderPerBatch={10} // Items per render batch
|
|
113
|
-
windowSize={5} // Render window (screens)
|
|
114
|
-
initialNumToRender={10} // Initial render count
|
|
115
|
-
updateCellsBatchingPeriod={50} // Batch update delay (ms)
|
|
116
|
-
|
|
117
|
-
// Prevent extra renders
|
|
118
|
-
extraData={selectedId} // Only re-render when this changes
|
|
119
|
-
/>
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## Pattern: FlashList for Large Lists
|
|
125
|
-
|
|
126
|
-
**When to use:** 1000+ items, complex item components, or FlatList still janky.
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
import { FlashList } from '@shopify/flash-list';
|
|
130
|
-
|
|
131
|
-
<FlashList
|
|
132
|
-
data={players}
|
|
133
|
-
renderItem={renderItem}
|
|
134
|
-
estimatedItemSize={80} // Required - estimate item height
|
|
135
|
-
keyExtractor={keyExtractor}
|
|
136
|
-
/>
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
**Note:** This codebase doesn't currently use FlashList. Consider for coach player lists.
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## Pattern: Memoization
|
|
144
|
-
|
|
145
|
-
### useMemo - Expensive Computations
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
// ✅ CORRECT: Memoize expensive calculation
|
|
149
|
-
const sortedAndFilteredItems = useMemo(() => {
|
|
150
|
-
return items
|
|
151
|
-
.filter(item => item.active)
|
|
152
|
-
.sort((a, b) => b.score - a.score)
|
|
153
|
-
.slice(0, 100);
|
|
154
|
-
}, [items]);
|
|
155
|
-
|
|
156
|
-
// ❌ WRONG: Recalculates every render
|
|
157
|
-
const sortedAndFilteredItems = items
|
|
158
|
-
.filter(item => item.active)
|
|
159
|
-
.sort((a, b) => b.score - a.score);
|
|
160
|
-
|
|
161
|
-
// ❌ WRONG: Memoizing simple access (overhead > benefit)
|
|
162
|
-
const userName = useMemo(() => user.name, [user.name]);
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
**When to use useMemo:**
|
|
166
|
-
|
|
167
|
-
- Array transformations (filter, sort, map chains)
|
|
168
|
-
- Object creation passed to memoized children
|
|
169
|
-
- Computations with O(n) or higher complexity
|
|
170
|
-
|
|
171
|
-
### useCallback - Stable Function References
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
// ✅ CORRECT: Stable callback for child props
|
|
175
|
-
const handlePress = useCallback((id: string) => {
|
|
176
|
-
setSelectedId(id);
|
|
177
|
-
}, []);
|
|
178
|
-
|
|
179
|
-
// Pass to memoized child
|
|
180
|
-
<MemoizedItem onPress={handlePress} />
|
|
181
|
-
|
|
182
|
-
// ❌ WRONG: useCallback with unstable deps
|
|
183
|
-
const handlePress = useCallback((id: string) => {
|
|
184
|
-
doSomething(unstableObject); // unstableObject changes every render
|
|
185
|
-
}, [unstableObject]); // Defeats the purpose
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**When to use useCallback:**
|
|
189
|
-
|
|
190
|
-
- Callbacks passed to memoized children
|
|
191
|
-
- Callbacks in dependency arrays
|
|
192
|
-
- Event handlers that would cause child re-renders
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
## Pattern: React.memo
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
// Wrap components that receive stable props
|
|
200
|
-
const PlayerCard = memo(function PlayerCard({
|
|
201
|
-
player,
|
|
202
|
-
onSelect
|
|
203
|
-
}: Props) {
|
|
204
|
-
return (
|
|
205
|
-
<Pressable onPress={() => onSelect(player.id)}>
|
|
206
|
-
<Text>{player.name}</Text>
|
|
207
|
-
<Text>{player.rating}</Text>
|
|
208
|
-
</Pressable>
|
|
209
|
-
);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
// Custom comparison for complex props
|
|
213
|
-
const PlayerCard = memo(
|
|
214
|
-
function PlayerCard({ player, onSelect }: Props) {
|
|
215
|
-
// ...
|
|
216
|
-
},
|
|
217
|
-
(prevProps, nextProps) => {
|
|
218
|
-
// Return true if props are equal (skip re-render)
|
|
219
|
-
return (
|
|
220
|
-
prevProps.player.id === nextProps.player.id &&
|
|
221
|
-
prevProps.player.rating === nextProps.player.rating
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
**When to use React.memo:**
|
|
228
|
-
|
|
229
|
-
- List item components
|
|
230
|
-
- Components receiving stable primitive props
|
|
231
|
-
- Components that render frequently but rarely change
|
|
232
|
-
|
|
233
|
-
**When NOT to use:**
|
|
234
|
-
|
|
235
|
-
- Components that always receive new props
|
|
236
|
-
- Simple components (overhead > benefit)
|
|
237
|
-
- Root-level screens
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## Pattern: Zustand Selector Optimization
|
|
242
|
-
|
|
243
|
-
**Problem:** Selecting entire store causes re-render on any state change.
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
// ❌ WRONG: Re-renders on ANY store change
|
|
247
|
-
const store = useAssessmentStore();
|
|
248
|
-
// or
|
|
249
|
-
const { userAnswers, isLoading, retakeAreas, ... } = useAssessmentStore();
|
|
250
|
-
|
|
251
|
-
// ✅ CORRECT: Only re-renders when selected values change
|
|
252
|
-
const userAnswers = useAssessmentStore((s) => s.userAnswers);
|
|
253
|
-
const isLoading = useAssessmentStore((s) => s.isLoading);
|
|
254
|
-
|
|
255
|
-
// ✅ CORRECT: Multiple values with shallow comparison
|
|
256
|
-
import { useShallow } from 'zustand/react/shallow';
|
|
257
|
-
|
|
258
|
-
const { userAnswers, isLoading } = useAssessmentStore(
|
|
259
|
-
useShallow((s) => ({
|
|
260
|
-
userAnswers: s.userAnswers,
|
|
261
|
-
isLoading: s.isLoading
|
|
262
|
-
}))
|
|
263
|
-
);
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
**See also:** `rn-zustand-patterns/SKILL.md` for more Zustand patterns.
|
|
267
|
-
|
|
268
|
-
---
|
|
269
|
-
|
|
270
|
-
## Pattern: Image Optimization
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
import { Image } from 'expo-image';
|
|
274
|
-
|
|
275
|
-
// expo-image provides caching and performance optimizations
|
|
276
|
-
<Image
|
|
277
|
-
source={{ uri: player.avatarUrl }}
|
|
278
|
-
style={{ width: 50, height: 50 }}
|
|
279
|
-
contentFit="cover"
|
|
280
|
-
placeholder={blurhash} // Show while loading
|
|
281
|
-
transition={200} // Fade in duration
|
|
282
|
-
cachePolicy="memory-disk" // Cache strategy
|
|
283
|
-
/>
|
|
284
|
-
|
|
285
|
-
// For lists, add priority
|
|
286
|
-
<Image
|
|
287
|
-
source={{ uri: player.avatarUrl }}
|
|
288
|
-
priority={isVisible ? 'high' : 'low'}
|
|
289
|
-
/>
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
---
|
|
293
|
-
|
|
294
|
-
## Pattern: Avoiding Re-Renders
|
|
295
|
-
|
|
296
|
-
### Object/Array Stability
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
// ❌ WRONG: New object every render
|
|
300
|
-
<ChildComponent style={{ padding: 10 }} />
|
|
301
|
-
<ChildComponent config={{ enabled: true }} />
|
|
302
|
-
|
|
303
|
-
// ✅ CORRECT: Stable reference
|
|
304
|
-
const style = useMemo(() => ({ padding: 10 }), []);
|
|
305
|
-
const config = useMemo(() => ({ enabled: true }), []);
|
|
306
|
-
|
|
307
|
-
<ChildComponent style={style} />
|
|
308
|
-
<ChildComponent config={config} />
|
|
309
|
-
|
|
310
|
-
// ✅ CORRECT: Or use StyleSheet
|
|
311
|
-
const styles = StyleSheet.create({
|
|
312
|
-
container: { padding: 10 },
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
<ChildComponent style={styles.container} />
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
### Children Stability
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
// ❌ WRONG: Inline function creates new element each render
|
|
322
|
-
<Parent>
|
|
323
|
-
{() => <Child />}
|
|
324
|
-
</Parent>
|
|
325
|
-
|
|
326
|
-
// ✅ CORRECT: Stable element
|
|
327
|
-
const child = useMemo(() => <Child />, [deps]);
|
|
328
|
-
<Parent>{child}</Parent>
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
---
|
|
332
|
-
|
|
333
|
-
## Pattern: Detecting Re-Renders
|
|
334
|
-
|
|
335
|
-
### React DevTools Profiler
|
|
336
|
-
|
|
337
|
-
1. Open React DevTools
|
|
338
|
-
2. Go to Profiler tab
|
|
339
|
-
3. Click record, interact, stop
|
|
340
|
-
4. Review "Flamegraph" for render times
|
|
341
|
-
5. Look for components rendering unnecessarily
|
|
342
|
-
|
|
343
|
-
### why-did-you-render
|
|
344
|
-
|
|
345
|
-
```typescript
|
|
346
|
-
// Setup in development
|
|
347
|
-
import React from 'react';
|
|
348
|
-
|
|
349
|
-
if (__DEV__) {
|
|
350
|
-
const whyDidYouRender = require('@welldone-software/why-did-you-render');
|
|
351
|
-
whyDidYouRender(React, {
|
|
352
|
-
trackAllPureComponents: true,
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Mark specific component for tracking
|
|
357
|
-
PlayerCard.whyDidYouRender = true;
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### Console Logging
|
|
361
|
-
|
|
362
|
-
```typescript
|
|
363
|
-
// Quick check for re-renders
|
|
364
|
-
function PlayerCard({ player }: Props) {
|
|
365
|
-
console.log('PlayerCard render:', player.id);
|
|
366
|
-
// ...
|
|
367
|
-
}
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
---
|
|
371
|
-
|
|
372
|
-
## Pattern: Heavy Computation Off Main Thread
|
|
373
|
-
|
|
374
|
-
**Problem:** JS thread blocked causes UI jank.
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
// ❌ WRONG: Blocks JS thread
|
|
378
|
-
const result = heavyComputation(data); // Takes 500ms
|
|
379
|
-
|
|
380
|
-
// ✅ CORRECT: Use InteractionManager
|
|
381
|
-
import { InteractionManager } from 'react-native';
|
|
382
|
-
|
|
383
|
-
InteractionManager.runAfterInteractions(() => {
|
|
384
|
-
const result = heavyComputation(data);
|
|
385
|
-
setResult(result);
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
// ✅ CORRECT: requestAnimationFrame for visual updates
|
|
389
|
-
requestAnimationFrame(() => {
|
|
390
|
-
// Update after current frame
|
|
391
|
-
});
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
|
-
## Performance Checklist
|
|
397
|
-
|
|
398
|
-
Before shipping list-heavy screens:
|
|
399
|
-
|
|
400
|
-
- [ ] FlatList has `keyExtractor` (stable callback)
|
|
401
|
-
- [ ] FlatList has `getItemLayout` (if fixed height)
|
|
402
|
-
- [ ] List items are memoized with `React.memo`
|
|
403
|
-
- [ ] Callbacks passed to items use `useCallback`
|
|
404
|
-
- [ ] Zustand selectors are specific (not whole store)
|
|
405
|
-
- [ ] Images use `expo-image` with caching
|
|
406
|
-
- [ ] No inline object/function props to memoized children
|
|
407
|
-
- [ ] Profiler shows no unnecessary re-renders
|
|
408
|
-
|
|
409
|
-
---
|
|
410
|
-
|
|
411
|
-
## Common Issues
|
|
412
|
-
|
|
413
|
-
| Issue | Solution |
|
|
414
|
-
| ------- | ---------- |
|
|
415
|
-
| List scroll jank | Add `getItemLayout`, memoize items |
|
|
416
|
-
| Component re-renders too often | Check selector specificity, memoize props |
|
|
417
|
-
| Slow initial render | Reduce `initialNumToRender`, defer computation |
|
|
418
|
-
| Memory growing | Check for state accumulation, image cache |
|
|
419
|
-
| UI freezes on interaction | Move computation off main thread |
|
|
420
|
-
|
|
421
|
-
---
|
|
422
|
-
|
|
423
|
-
## Relationship to Other Skills
|
|
424
|
-
|
|
425
|
-
- **react-native-zustand-patterns**: Selector optimization patterns
|