ginskill-init 1.0.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 +77 -0
- package/agents/developer.md +56 -0
- package/agents/frontend-design.md +69 -0
- package/agents/mobile-reviewer.md +36 -0
- package/agents/review-code.md +49 -0
- package/agents/security-scanner.md +50 -0
- package/agents/tester.md +72 -0
- package/bin/cli.js +226 -0
- package/package.json +20 -0
- package/skills/ai-asset-generator/SKILL.md +255 -0
- package/skills/ai-asset-generator/docs/gen-image.md +274 -0
- package/skills/ai-asset-generator/docs/genvideo.md +341 -0
- package/skills/ai-asset-generator/docs/remove-background.md +19 -0
- package/skills/ai-asset-generator/generate-credit-assets.mjs +180 -0
- package/skills/ai-asset-generator/generate-ginbrowser-assets.mjs +242 -0
- package/skills/ai-asset-generator/generate-sty-icon.mjs +149 -0
- package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
- package/skills/ai-asset-generator/lib/env.mjs +38 -0
- package/skills/ai-asset-generator/lib/kie-client.mjs +88 -0
- package/skills/ai-asset-generator/scripts/scaffold-generator.mjs +203 -0
- package/skills/ai-build-ai/SKILL.md +124 -0
- package/skills/ai-build-ai/docs/agent-teams.md +293 -0
- package/skills/ai-build-ai/docs/checkpointing.md +161 -0
- package/skills/ai-build-ai/docs/create-agent.md +399 -0
- package/skills/ai-build-ai/docs/create-mcp.md +395 -0
- package/skills/ai-build-ai/docs/create-skill.md +299 -0
- package/skills/ai-build-ai/docs/headless-mode.md +614 -0
- package/skills/ai-build-ai/docs/hooks.md +578 -0
- package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
- package/skills/ai-build-ai/docs/output-styles.md +208 -0
- package/skills/ai-build-ai/docs/overview.md +162 -0
- package/skills/ai-build-ai/docs/permissions.md +391 -0
- package/skills/ai-build-ai/docs/plugins.md +396 -0
- package/skills/ai-build-ai/docs/sandbox.md +262 -0
- package/skills/ai-build-ai/scripts/load-tutorial.sh +54 -0
- package/skills/icon-generator/SKILL.md +270 -0
- package/skills/mobile-app-review/SKILL.md +321 -0
- package/skills/mobile-app-review/references/apple-review.md +132 -0
- package/skills/mobile-app-review/references/google-play-review.md +203 -0
- package/skills/mongodb/SKILL.md +667 -0
- package/skills/mongodb/references/mongoose-patterns.md +368 -0
- package/skills/nestjs-architecture/SKILL.md +1086 -0
- package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
- package/skills/performance/SKILL.md +509 -0
- package/skills/react-fsd-architecture/SKILL.md +693 -0
- package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
- package/skills/react-query/SKILL.md +685 -0
- package/skills/react-query/references/query-patterns.md +365 -0
- package/skills/review-code/SKILL.md +321 -0
- package/skills/review-code/references/clean-code-principles.md +395 -0
- package/skills/review-code/references/frontend-patterns.md +136 -0
- package/skills/review-code/references/nestjs-patterns.md +184 -0
- package/skills/review-code/scripts/check-module.sh +201 -0
- package/skills/review-code/scripts/deep-scan.sh +604 -0
- package/skills/review-code/scripts/dep-check.sh +522 -0
- package/skills/review-code/scripts/detect-duplicates.sh +466 -0
- package/skills/review-code/scripts/format-check.sh +577 -0
- package/skills/review-code/scripts/run-review.sh +167 -0
- package/skills/review-code/scripts/scan-codebase.sh +152 -0
- package/skills/security-scanner/SKILL.md +327 -0
- package/skills/security-scanner/references/nestjs-security.md +260 -0
- package/skills/security-scanner/references/nextjs-security.md +201 -0
- package/skills/security-scanner/references/react-native-security.md +199 -0
- package/skills/security-scanner/scripts/security-scan.sh +478 -0
- package/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/ui-ux-pro-max/scripts/search.py +114 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
# React Native Performance Optimization Skill
|
|
2
|
+
|
|
3
|
+
> Hướng dẫn toàn diện tối ưu hiệu suất React Native app (2025-2026)
|
|
4
|
+
|
|
5
|
+
## Khi nào kích hoạt skill này
|
|
6
|
+
|
|
7
|
+
- User hỏi về tối ưu hiệu suất React Native
|
|
8
|
+
- Code review phát hiện anti-patterns ảnh hưởng performance
|
|
9
|
+
- User muốn cải thiện startup time, FPS, memory, bundle size
|
|
10
|
+
- User cần chọn thư viện (state management, list, image, animation, navigation, storage)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Architecture Foundation (New Architecture)
|
|
15
|
+
|
|
16
|
+
### JSI (JavaScript Interface)
|
|
17
|
+
- Thay thế Bridge cũ bằng C++ direct calls, không serialization JSON
|
|
18
|
+
- **Benchmark**: ~55% faster startup vs JSC, 92-99.98% faster native method calls
|
|
19
|
+
- Cold TTI: 3.6s → 2.1s trên mid-range Android (Hermes + JSI + inlineRequires)
|
|
20
|
+
|
|
21
|
+
### Fabric Renderer
|
|
22
|
+
- Synchronous rendering via JSI, hỗ trợ React Concurrent Mode
|
|
23
|
+
- Yoga 2 layout engine nhanh hơn
|
|
24
|
+
- 5,000 `<Text>` elements render nhanh hơn 20%
|
|
25
|
+
|
|
26
|
+
### TurboModules
|
|
27
|
+
- Lazy loading: module chỉ init khi được gọi lần đầu
|
|
28
|
+
- `TurboModuleRegistry.get<Spec>('ModuleName')` — lazy by design
|
|
29
|
+
- Defer analytics, crash reporting, push notification init sau startup
|
|
30
|
+
|
|
31
|
+
### Hermes Engine (V1 — RN 0.82+/0.84 default)
|
|
32
|
+
- AOT bytecode precompilation: skip parse + compile at runtime
|
|
33
|
+
- TTI improvement: iOS +2.5%, Android +7.6% (vs Hermes legacy)
|
|
34
|
+
- Bundle load: iOS +9%, Android +3.1% faster
|
|
35
|
+
- Bytecode ~33% smaller than minified JS
|
|
36
|
+
- Memory: ~136MB vs JSC ~185MB (-26%)
|
|
37
|
+
|
|
38
|
+
### Bridgeless Mode (RN 0.76+ default)
|
|
39
|
+
- ~50% TTI reduction by removing legacy Bridge runtime init
|
|
40
|
+
- Timers, error handling, event emitters chuyển sang JSI
|
|
41
|
+
|
|
42
|
+
### Enable New Architecture
|
|
43
|
+
```js
|
|
44
|
+
// android/gradle.properties
|
|
45
|
+
newArchEnabled=true
|
|
46
|
+
|
|
47
|
+
// ios/Podfile
|
|
48
|
+
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Metro Config tối ưu
|
|
52
|
+
```js
|
|
53
|
+
// metro.config.js
|
|
54
|
+
module.exports = {
|
|
55
|
+
transformer: {
|
|
56
|
+
getTransformOptions: async () => ({
|
|
57
|
+
transform: {
|
|
58
|
+
inlineRequires: true, // defer module evaluation — major startup win
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 2. Rendering Optimization
|
|
68
|
+
|
|
69
|
+
### Giảm Re-render
|
|
70
|
+
|
|
71
|
+
**React.memo** — chỉ hiệu quả khi MỌI prop stable:
|
|
72
|
+
```tsx
|
|
73
|
+
const ProductCard = React.memo(({ product, onPress }: Props) => {
|
|
74
|
+
return (
|
|
75
|
+
<Pressable onPress={onPress}>
|
|
76
|
+
<Text>{product.name}</Text>
|
|
77
|
+
</Pressable>
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**useCallback** — stabilize callback trước khi truyền vào memo child:
|
|
83
|
+
```tsx
|
|
84
|
+
const handlePress = useCallback((id: string) => {
|
|
85
|
+
dispatch(addToCart(id));
|
|
86
|
+
}, [dispatch]);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**useMemo** — chỉ dùng cho expensive computation:
|
|
90
|
+
```tsx
|
|
91
|
+
const sorted = useMemo(
|
|
92
|
+
() => products.filter(p => p.inStock).sort((a, b) => a.price - b.price),
|
|
93
|
+
[products],
|
|
94
|
+
);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**React Compiler (RN 0.78+ / Expo SDK 53+)**: Tự động memoize, giảm nhu cầu manual useMemo/useCallback.
|
|
98
|
+
|
|
99
|
+
### Lists: FlashList > FlatList
|
|
100
|
+
|
|
101
|
+
| Metric | FlatList | FlashList |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| JS thread FPS | baseline | ~7.5x better |
|
|
104
|
+
| CPU usage | baseline | -32% |
|
|
105
|
+
| Blank area | baseline | -50% (v2) |
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import { FlashList } from '@shopify/flash-list';
|
|
109
|
+
|
|
110
|
+
<FlashList
|
|
111
|
+
data={products}
|
|
112
|
+
renderItem={({ item }) => <ProductCard product={item} />}
|
|
113
|
+
keyExtractor={item => item.id}
|
|
114
|
+
estimatedItemSize={88} // v1 required; v2 optional
|
|
115
|
+
/>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**FlatList optimization khi cần dùng:**
|
|
119
|
+
```tsx
|
|
120
|
+
<FlatList
|
|
121
|
+
data={products}
|
|
122
|
+
renderItem={renderItem} // useCallback, defined outside JSX
|
|
123
|
+
keyExtractor={item => item.id} // NEVER use index
|
|
124
|
+
getItemLayout={(_, index) => ({ length: 88, offset: 88 * index, index })}
|
|
125
|
+
maxToRenderPerBatch={8}
|
|
126
|
+
windowSize={11}
|
|
127
|
+
updateCellsBatchingPeriod={40}
|
|
128
|
+
initialNumToRender={6}
|
|
129
|
+
removeClippedSubviews={Platform.OS === 'android'}
|
|
130
|
+
/>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Image Optimization
|
|
134
|
+
|
|
135
|
+
**Recommended libraries:**
|
|
136
|
+
- **expo-image**: Best cho Expo (WebP, AVIF, Blurhash built-in)
|
|
137
|
+
- **@d11/react-native-fast-image**: Best cho bare RN
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
import { Image } from 'expo-image';
|
|
141
|
+
|
|
142
|
+
<Image
|
|
143
|
+
source={{ uri: `${baseUrl}?w=400&h=400&fm=webp&q=75` }}
|
|
144
|
+
placeholder={{ blurhash: 'LEHV6nWB2yk8...' }}
|
|
145
|
+
contentFit="cover"
|
|
146
|
+
transition={200}
|
|
147
|
+
cachePolicy="memory-disk"
|
|
148
|
+
style={{ width: 200, height: 200 }}
|
|
149
|
+
/>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Format priority**: AVIF > WebP > JPEG. Dùng CDN với `f_auto,q_auto`.
|
|
153
|
+
|
|
154
|
+
**CRITICAL**: Android decode 1080x1920 JPEG → ~8MB RAM. LUÔN request thumbnail size từ CDN.
|
|
155
|
+
|
|
156
|
+
### Animation: Reanimated 3/4
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
|
|
160
|
+
|
|
161
|
+
const scale = useSharedValue(1);
|
|
162
|
+
|
|
163
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
164
|
+
transform: [{ scale: scale.value }],
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
// Worklets run on UI thread — 60fps guaranteed
|
|
168
|
+
const onPressIn = () => { scale.value = withSpring(0.95); };
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
| | Animated (no native) | Animated (useNativeDriver) | Reanimated 3/4 |
|
|
172
|
+
|---|---|---|---|
|
|
173
|
+
| FPS under JS load | ~45fps | ~60fps | 60-120fps |
|
|
174
|
+
| Layout properties | ~30fps | Not supported | ~60fps |
|
|
175
|
+
| Gesture tracking | Laggy | Partial | 60-120fps |
|
|
176
|
+
|
|
177
|
+
**Lottie**: Dùng `.lottie` format (70-80% smaller than JSON). Unmount khi không visible.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 3. Startup Optimization
|
|
182
|
+
|
|
183
|
+
### Cold Start Targets
|
|
184
|
+
|
|
185
|
+
| Device | Target TTI |
|
|
186
|
+
|---|---|
|
|
187
|
+
| iOS flagship | ≤ 1.5s |
|
|
188
|
+
| Mid-tier Android | ≤ 2.0s |
|
|
189
|
+
| Low-end Android | ≤ 2.5s |
|
|
190
|
+
|
|
191
|
+
### Quick Wins
|
|
192
|
+
1. **Enable Hermes** (default RN 0.70+, verify active)
|
|
193
|
+
2. **Enable inlineRequires** trong metro.config.js
|
|
194
|
+
3. **react-native-bootsplash** thay react-native-splash-screen
|
|
195
|
+
4. **Bundle visualizer**: `npx react-native-bundle-visualizer`
|
|
196
|
+
5. **Replace heavy libs**: moment.js → dayjs, lodash → lodash/method
|
|
197
|
+
|
|
198
|
+
### Lazy Loading
|
|
199
|
+
```tsx
|
|
200
|
+
// Screens
|
|
201
|
+
const HeavyScreen = React.lazy(() => import('./screens/HeavyScreen'));
|
|
202
|
+
|
|
203
|
+
// Navigation
|
|
204
|
+
<Stack.Navigator screenOptions={{ lazy: true }}>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Defer Non-Critical Modules
|
|
208
|
+
- Analytics, crash reporting → init sau startup
|
|
209
|
+
- Push notification setup → setTimeout hoặc InteractionManager
|
|
210
|
+
- Social SDK → lazy TurboModule
|
|
211
|
+
|
|
212
|
+
### Splash Screen → Skeleton → Content
|
|
213
|
+
```tsx
|
|
214
|
+
import BootSplash from 'react-native-bootsplash';
|
|
215
|
+
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
Promise.all([loadFonts(), checkAuth(), prefetchData()])
|
|
218
|
+
.finally(() => BootSplash.hide({ fade: true }));
|
|
219
|
+
}, []);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 4. Memory Management
|
|
225
|
+
|
|
226
|
+
### Top Memory Leak Causes & Fixes
|
|
227
|
+
|
|
228
|
+
**1. Event Listeners** — phải cleanup:
|
|
229
|
+
```tsx
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
const sub = Keyboard.addListener('keyboardDidShow', handler);
|
|
232
|
+
return () => sub.remove();
|
|
233
|
+
}, []);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**2. Timers** — phải clearInterval/clearTimeout:
|
|
237
|
+
```tsx
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
const id = setInterval(fetchPrice, 2000);
|
|
240
|
+
return () => clearInterval(id);
|
|
241
|
+
}, []);
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**3. Async Operations** — dùng AbortController:
|
|
245
|
+
```tsx
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
const controller = new AbortController();
|
|
248
|
+
fetch(url, { signal: controller.signal }).then(/*...*/);
|
|
249
|
+
return () => controller.abort();
|
|
250
|
+
}, []);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**4. Closures** — capture primitive, không capture entire object.
|
|
254
|
+
|
|
255
|
+
### Image Memory
|
|
256
|
+
- Android: 1080x1920 JPEG → ~8MB RAM per image
|
|
257
|
+
- LUÔN request thumbnail size từ CDN/server
|
|
258
|
+
- Clear memory cache khi unmount heavy gallery screens
|
|
259
|
+
|
|
260
|
+
### Memory Budgets
|
|
261
|
+
|
|
262
|
+
| Device RAM | JS Heap Budget | Image Cache Budget |
|
|
263
|
+
|---|---|---|
|
|
264
|
+
| 1-2 GB | < 50 MB | < 30 MB |
|
|
265
|
+
| 3-4 GB | < 100 MB | < 80 MB |
|
|
266
|
+
| 6+ GB | < 200 MB | < 200 MB |
|
|
267
|
+
|
|
268
|
+
### Large Data → Pagination, không giữ trong memory
|
|
269
|
+
```tsx
|
|
270
|
+
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
|
|
271
|
+
queryKey: ['products'],
|
|
272
|
+
queryFn: ({ pageParam = 0 }) => fetchProducts(pageParam),
|
|
273
|
+
getNextPageParam: (lastPage, pages) =>
|
|
274
|
+
lastPage.length === PAGE_SIZE ? pages.length * PAGE_SIZE : undefined,
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Storage: MMKV >> AsyncStorage
|
|
279
|
+
- MMKV: ~20-30x faster reads, ~500% faster writes
|
|
280
|
+
- Synchronous (JSI), encryption support
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
import { MMKV } from 'react-native-mmkv';
|
|
284
|
+
const storage = new MMKV({ id: 'app-storage' });
|
|
285
|
+
storage.set('token', value);
|
|
286
|
+
storage.getString('token');
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 5. Navigation Optimization
|
|
292
|
+
|
|
293
|
+
### Library Choice
|
|
294
|
+
|
|
295
|
+
| Need | Recommendation |
|
|
296
|
+
|---|---|
|
|
297
|
+
| Standard app | `@react-navigation/native-stack` + `react-native-screens` |
|
|
298
|
+
| File-based routing + web | Expo Router |
|
|
299
|
+
| Max native performance | react-native-navigation (Wix) |
|
|
300
|
+
|
|
301
|
+
### Key Optimizations
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
// Entry point
|
|
305
|
+
import { enableScreens } from 'react-native-screens';
|
|
306
|
+
enableScreens(true); // native screen containers — saves 30-40% memory
|
|
307
|
+
|
|
308
|
+
// Tab Navigator
|
|
309
|
+
<Tab.Navigator screenOptions={{ lazy: true, lazyPreloadDistance: 0 }}>
|
|
310
|
+
|
|
311
|
+
// ALWAYS use native-stack, NOT JS stack
|
|
312
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### useFocusEffect vs useEffect
|
|
316
|
+
|
|
317
|
+
| | useEffect | useFocusEffect |
|
|
318
|
+
|---|---|---|
|
|
319
|
+
| Trigger | Mount/unmount | Screen focus/blur |
|
|
320
|
+
| Runs khi quay lại screen | No | Yes |
|
|
321
|
+
| Use case | One-time setup | Data refresh, analytics |
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
useFocusEffect(
|
|
325
|
+
useCallback(() => {
|
|
326
|
+
fetchFreshData();
|
|
327
|
+
return () => cleanup();
|
|
328
|
+
}, [])
|
|
329
|
+
);
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Tránh heavy work khi navigate
|
|
333
|
+
```tsx
|
|
334
|
+
useFocusEffect(
|
|
335
|
+
useCallback(() => {
|
|
336
|
+
const task = InteractionManager.runAfterInteractions(async () => {
|
|
337
|
+
// Heavy work SAU KHI animation complete
|
|
338
|
+
});
|
|
339
|
+
return () => task.cancel();
|
|
340
|
+
}, [])
|
|
341
|
+
);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Navigation depth: MAX 3 levels. Flatten bằng root-level screens cho modals/details.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 6. Network & State Management
|
|
349
|
+
|
|
350
|
+
### State Management Choice
|
|
351
|
+
|
|
352
|
+
| Library | Bundle | Update Speed | Best For |
|
|
353
|
+
|---|---|---|---|
|
|
354
|
+
| **Zustand** | ~8 KB | 0.8ms | Most apps, simple API, MMKV persist |
|
|
355
|
+
| **Jotai** | ~4 KB | 0.9ms | Complex derived/interdependent state |
|
|
356
|
+
| **Redux Toolkit** | ~43 KB | 1.2ms | Large teams, complex async, RTK Query |
|
|
357
|
+
| Context API | 0 KB | 2.5ms+ | Theme, locale, auth (low-frequency only) |
|
|
358
|
+
|
|
359
|
+
**Zustand selector pattern:**
|
|
360
|
+
```tsx
|
|
361
|
+
// Only re-renders when `total` changes
|
|
362
|
+
const total = useCartStore((state) => state.total);
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Data Fetching: TanStack Query
|
|
366
|
+
|
|
367
|
+
```tsx
|
|
368
|
+
const queryClient = new QueryClient({
|
|
369
|
+
defaultOptions: {
|
|
370
|
+
queries: {
|
|
371
|
+
staleTime: 5 * 60 * 1000, // 5 min fresh
|
|
372
|
+
gcTime: 24 * 60 * 60 * 1000, // 24h cache
|
|
373
|
+
retry: 2,
|
|
374
|
+
refetchOnWindowFocus: false, // mobile: use AppState instead
|
|
375
|
+
refetchOnReconnect: true,
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Offline persistence với MMKV:**
|
|
382
|
+
```tsx
|
|
383
|
+
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';
|
|
384
|
+
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
|
|
385
|
+
|
|
386
|
+
const persister = createSyncStoragePersister({
|
|
387
|
+
storage: {
|
|
388
|
+
getItem: (key) => storage.getString(key) ?? null,
|
|
389
|
+
setItem: (key, value) => storage.set(key, value),
|
|
390
|
+
removeItem: (key) => storage.delete(key),
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Network Best Practices
|
|
396
|
+
- `Promise.all()` cho parallel requests (tránh waterfall)
|
|
397
|
+
- Field projection hoặc GraphQL (tránh over-fetching)
|
|
398
|
+
- WebSocket cho real-time (tránh polling)
|
|
399
|
+
- Exponential backoff cho reconnection
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## 7. Anti-Patterns Quick Reference
|
|
404
|
+
|
|
405
|
+
| Anti-Pattern | Fix |
|
|
406
|
+
|---|---|
|
|
407
|
+
| Inline `() => {}` trong JSX props | `useCallback` + `React.memo` |
|
|
408
|
+
| Inline `{}` style objects | `StyleSheet.create` + `useMemo` dynamic parts |
|
|
409
|
+
| `key={index}` trong lists | `key={item.id}` (stable unique ID) |
|
|
410
|
+
| Monolithic Context cho mọi state | Split context; Zustand cho high-frequency |
|
|
411
|
+
| FlatList inside ScrollView | `ListHeaderComponent` / FlashList |
|
|
412
|
+
| Component 300+ lines | Decompose thành focused leaf components |
|
|
413
|
+
| `import _ from 'lodash'` | `import get from 'lodash/get'` |
|
|
414
|
+
| All screens imported eagerly | `React.lazy()` + `Suspense` |
|
|
415
|
+
| All tabs rendered on mount | `screenOptions={{ lazy: true }}` |
|
|
416
|
+
| Heavy work trong useFocusEffect | `InteractionManager.runAfterInteractions` |
|
|
417
|
+
| Navigator nesting >3 levels | Flatten, root-level stack cho modals |
|
|
418
|
+
| No API caching | TanStack Query với staleTime |
|
|
419
|
+
| Sequential await chains | `Promise.all()` hoặc BFF endpoint |
|
|
420
|
+
| `setInterval` polling | WebSocket singleton |
|
|
421
|
+
| Missing useEffect cleanup | Return cleanup; AbortController |
|
|
422
|
+
| Full-res images cho thumbnails | CDN resize; request đúng size |
|
|
423
|
+
| Entire dataset trong state | Cursor-based pagination |
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## 8. Performance Budgets
|
|
428
|
+
|
|
429
|
+
| Metric | Good | Warning | Fail |
|
|
430
|
+
|---|---|---|---|
|
|
431
|
+
| Cold Start TTI | ≤ 1.5s iOS / ≤ 2.0s Android | ≤ 2.5s | > 3.0s |
|
|
432
|
+
| Screen Transition | ≤ 300ms | ≤ 500ms | > 700ms |
|
|
433
|
+
| List Scroll FPS | 60 FPS | 50-59 FPS | < 50 FPS |
|
|
434
|
+
| Memory Peak | ≤ 200MB (3GB device) | ≤ 350MB | > 400MB |
|
|
435
|
+
| JS Bundle (gzipped) | ≤ 2 MB | ≤ 4 MB | > 6 MB |
|
|
436
|
+
| API → First Render | ≤ 300ms WiFi | ≤ 1.0s | > 1.5s |
|
|
437
|
+
| App Install Size | ≤ 20 MB AAB | ≤ 50 MB | > 80 MB |
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 9. Monitoring & Profiling Tools
|
|
442
|
+
|
|
443
|
+
| Need | Tool |
|
|
444
|
+
|---|---|
|
|
445
|
+
| Dev JS debugging | React Native DevTools (RN 0.76+, press `j` in Metro) |
|
|
446
|
+
| Native iOS profiling | Xcode Instruments (Time Profiler + Leaks) |
|
|
447
|
+
| Native Android profiling | Android Studio Profiler (CPU System Trace) |
|
|
448
|
+
| Production monitoring | Sentry React Native SDK |
|
|
449
|
+
| Custom traces (TTI) | Firebase Performance / `react-native-performance` |
|
|
450
|
+
| Bundle analysis | `react-native-bundle-visualizer` / Expo Atlas |
|
|
451
|
+
| Render regression CI | Reassure (Callstack) |
|
|
452
|
+
| Android perf scoring CI | Flashlight (BAM/Theodo) |
|
|
453
|
+
| E2E testing | Maestro (preferred) / Detox |
|
|
454
|
+
| Memory leak (Android) | LeakCanary |
|
|
455
|
+
| Memory leak (iOS) | Xcode Instruments Leaks |
|
|
456
|
+
|
|
457
|
+
### Sentry Setup
|
|
458
|
+
```tsx
|
|
459
|
+
import * as Sentry from '@sentry/react-native';
|
|
460
|
+
|
|
461
|
+
Sentry.init({
|
|
462
|
+
dsn: 'YOUR_DSN',
|
|
463
|
+
tracesSampleRate: 0.15,
|
|
464
|
+
profilesSampleRate: 0.1,
|
|
465
|
+
enableNativeFramesTracking: true,
|
|
466
|
+
enableStallTracking: true,
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## 10. Optimization Checklist by Effort Level
|
|
473
|
+
|
|
474
|
+
### Level 1 — Quick Wins (< 1 day)
|
|
475
|
+
- [ ] Verify Hermes enabled
|
|
476
|
+
- [ ] Enable `inlineRequires` in metro.config.js
|
|
477
|
+
- [ ] Switch to react-native-bootsplash
|
|
478
|
+
- [ ] Run bundle visualizer, replace heavy deps (moment→dayjs)
|
|
479
|
+
- [ ] Add `keyExtractor` with stable IDs to all lists
|
|
480
|
+
- [ ] Move static styles to `StyleSheet.create()`
|
|
481
|
+
- [ ] Enable `lazy: true` on tab navigators
|
|
482
|
+
- [ ] Replace AsyncStorage with MMKV
|
|
483
|
+
|
|
484
|
+
### Level 2 — Standard (1-3 days)
|
|
485
|
+
- [ ] Replace FlatList with FlashList for large lists
|
|
486
|
+
- [ ] Add React.memo + useCallback cho list items
|
|
487
|
+
- [ ] Switch to expo-image / @d11/react-native-fast-image
|
|
488
|
+
- [ ] Setup TanStack Query with staleTime + MMKV persist
|
|
489
|
+
- [ ] Lazy load screens with React.lazy + Suspense
|
|
490
|
+
- [ ] Defer analytics/crash reporting init post-startup
|
|
491
|
+
- [ ] Add useEffect cleanup cho tất cả subscriptions
|
|
492
|
+
- [ ] Request thumbnail sizes từ CDN
|
|
493
|
+
|
|
494
|
+
### Level 3 — Advanced (1-2 weeks)
|
|
495
|
+
- [ ] Migrate to New Architecture (RN 0.76+)
|
|
496
|
+
- [ ] Replace Animated API with Reanimated 3/4
|
|
497
|
+
- [ ] Setup Sentry performance monitoring
|
|
498
|
+
- [ ] Add Reassure render benchmarks to CI
|
|
499
|
+
- [ ] Implement cursor-based pagination
|
|
500
|
+
- [ ] Setup WebSocket singleton cho real-time features
|
|
501
|
+
- [ ] Flatten navigation depth to ≤ 3 levels
|
|
502
|
+
|
|
503
|
+
### Level 4 — Expert (> 2 weeks)
|
|
504
|
+
- [ ] Custom TurboModules for heavy native operations
|
|
505
|
+
- [ ] Implement performance budgets in CI pipeline
|
|
506
|
+
- [ ] Setup Flashlight + Maestro for automated perf testing
|
|
507
|
+
- [ ] Evaluate React Compiler (RN 0.78+)
|
|
508
|
+
- [ ] RAM Bundles / Re.Pack for large apps
|
|
509
|
+
- [ ] Upgrade to Hermes V1 (RN 0.82+)
|