1k-performance
Performance optimization guidelines for OneKey React/React Native applications. Use when optimizing app performance, fixing UI freezes/lag, reducing re-renders, handling concurrent operations, or analyzing performance bottlenecks. Triggers on performance, optimization, optimize, slow, lag, freeze, hang, jank, stutter, memory, leak, concurrent, batching, batch, memoization, memo, bridge, windowSize, contentVisibility, FlashList, re-render, fps, tti, bundle.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install onekeyhq-app-monorepo-1k-performance
Repository
Skill path: .claude/skills/1k-performance
Performance optimization guidelines for OneKey React/React Native applications. Use when optimizing app performance, fixing UI freezes/lag, reducing re-renders, handling concurrent operations, or analyzing performance bottlenecks. Triggers on performance, optimization, optimize, slow, lag, freeze, hang, jank, stutter, memory, leak, concurrent, batching, batch, memoization, memo, bridge, windowSize, contentVisibility, FlashList, re-render, fps, tti, bundle.
Open repositoryBest for
Primary workflow: Build Mobile.
Technical facets: Full Stack, Frontend, Mobile.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: OneKeyHQ.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install 1k-performance into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/OneKeyHQ/app-monorepo before adding 1k-performance to shared team environments
- Use 1k-performance for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: 1k-performance
description: Performance optimization guidelines for OneKey React/React Native applications. Use when optimizing app performance, fixing UI freezes/lag, reducing re-renders, handling concurrent operations, or analyzing performance bottlenecks. Triggers on performance, optimization, optimize, slow, lag, freeze, hang, jank, stutter, memory, leak, concurrent, batching, batch, memoization, memo, bridge, windowSize, contentVisibility, FlashList, re-render, fps, tti, bundle.
allowed-tools: Read, Grep, Glob
---
# OneKey Performance Optimization
Performance optimization patterns and best practices for React/React Native applications in the OneKey monorepo.
## Quick Reference
| Category | Key Optimization | When to Use |
|----------|------------------|-------------|
| **Concurrent Requests** | Limit to 3-5, use `executeBatched` | Multiple API calls, network-heavy operations |
| **Bridge Optimization** | Minimize crossings, batch data | React Native bridge overhead, iOS/Android |
| **List Rendering** | FlashList, windowSize={5}, content-visibility | Lists with 100+ items |
| **Memoization** | memo, useMemo, useCallback | Expensive computations, prevent re-renders |
| **Heavy Operations** | InteractionManager, setTimeout | UI blocking operations |
## Critical Performance Rules
### ā FORBIDDEN: Too Many Concurrent Requests
```typescript
// ā BAD - Can freeze UI with 15+ requests
const requests = items.map(item => fetchData(item));
await Promise.all(requests);
```
### ā
CORRECT: Batched Execution with Concurrency Limit
```typescript
async function executeBatched<T>(
tasks: Array<() => Promise<T>>,
concurrency = 3,
): Promise<Array<PromiseSettledResult<T>>> {
const results: Array<PromiseSettledResult<T>> = [];
for (let i = 0; i < tasks.length; i += concurrency) {
const batch = tasks.slice(i, i + concurrency);
const batchResults = await Promise.allSettled(
batch.map((task) => task()),
);
results.push(...batchResults);
}
return results;
}
const tasks = items.map(item => () => fetchData(item));
await executeBatched(tasks, 3); // Max 3 concurrent
```
## šØ Built-in Optimizations
**Already Optimized - NO ACTION NEEDED:**
| Component | Optimization | Details |
|-----------|--------------|---------|
| `ListView` | `windowSize={5}` | Auto-limits visible items |
| `Tabs` | `contentVisibility: 'hidden'` | Hides inactive tabs |
| `Dialog` | `contentVisibility: 'hidden'` | Hides when closed |
## Detailed Guide
For comprehensive performance optimization strategies, see [performance.md](references/rules/performance.md).
Topics covered:
- Concurrent request control
- React Native bridge optimization
- Heavy operations offloading
- List rendering (windowSize, FlashList, content-visibility)
- Memoization & callbacks
- State updates optimization
- Image optimization
- Async operations & race conditions
- Real-world iOS AppHang case study
## Related Skills
- `/1k-coding-patterns` - General coding patterns and conventions
- `/1k-sentry-analysis` - Sentry error analysis (includes performance issues)
- `/react-native-best-practices` - React Native specific optimizations
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/rules/performance.md
```markdown
# Performance Optimization
Performance best practices for OneKey development, focusing on React Native, React, and cross-platform optimization.
## Critical Performance Rules
### 1. Concurrent Request Control
**Problem**: Too many simultaneous network requests can block the UI thread via React Native bridge saturation.
**Rule**: Always limit concurrent network requests to prevent bridge message queue overflow.
```typescript
// ā BAD - All requests fire simultaneously
const requests = items.map(item => fetchData(item));
await Promise.all(requests); // Can cause UI freeze with 15+ requests
// ā
GOOD - Batched execution with concurrency limit
async function executeBatched<T>(
tasks: Array<() => Promise<T>>,
concurrency = 3,
): Promise<Array<PromiseSettledResult<T>>> {
const results: Array<PromiseSettledResult<T>> = [];
for (let i = 0; i < tasks.length; i += concurrency) {
const batch = tasks.slice(i, i + concurrency);
const batchResults = await Promise.allSettled(
batch.map((task) => task()),
);
results.push(...batchResults);
}
return results;
}
// Usage
const tasks = items.map(item => () => fetchData(item));
const results = await executeBatched(tasks, 3); // Max 3 concurrent
```
**Why this matters**:
- Each network request generates 6+ React Native bridge messages
- 15 concurrent requests = 90+ bridge messages = UI freeze
- iPhone 7 and older devices are especially vulnerable
- iOS Watchdog kills app after 5s freeze (AppHang)
**When to use**:
- Loading data for multiple networks/accounts
- Batch operations (bulk updates, sync operations)
- Any scenario with 5+ potential concurrent requests
### 2. React Native Bridge Optimization
**Problem**: The React Native bridge is serialized - only one message crosses at a time. High bridge traffic delays UI updates.
**Rules**:
#### Minimize Bridge Crossings
```typescript
// ā BAD - Multiple bridge crossings
items.forEach(item => {
NativeModules.MyModule.update(item.id, item.value); // Each call = bridge crossing
});
// ā
GOOD - Single bridge crossing
NativeModules.MyModule.batchUpdate(items); // Pass all data at once
```
#### Avoid Large Data Transfers
```typescript
// ā BAD - Transferring large objects
const hugeData = await fetchLargeDataset(); // 10MB JSON
setState(hugeData); // Serialization cost + bridge crossing
// ā
GOOD - Paginate or lazy load
const page1 = await fetchPage(1, 50); // 100KB chunks
setState(page1);
// Load more on demand
```
#### Debounce Frequent Updates
```typescript
// ā BAD - Every keystroke crosses bridge
<TextInput
onChangeText={(text) => {
NativeModules.Analytics.trackInput(text); // Bridge spam
}}
/>
// ā
GOOD - Debounced updates
import { debounce } from 'lodash';
const trackInput = debounce((text) => {
NativeModules.Analytics.trackInput(text);
}, 500);
<TextInput onChangeText={trackInput} />
```
### 3. Main Thread Protection
**Problem**: JavaScript operations can block the main thread, causing UI jank and freezes.
**Rules**:
#### Defer Heavy Operations
```typescript
// ā BAD - Heavy work during render/navigation
function MyScreen() {
const data = processLargeDataset(); // Blocks render
return <View>{data}</View>;
}
// ā
GOOD - Defer to next tick
function MyScreen() {
const [data, setData] = useState(null);
useEffect(() => {
// Let UI render first
setTimeout(() => {
setData(processLargeDataset());
}, 0);
}, []);
if (!data) return <Spinner />;
return <View>{data}</View>;
}
// ā
BETTER - Use InteractionManager (React Native)
import { InteractionManager } from 'react-native';
useEffect(() => {
const task = InteractionManager.runAfterInteractions(() => {
setData(processLargeDataset());
});
return () => task.cancel();
}, []);
```
#### Avoid Synchronous Heavy Computation
```typescript
// ā BAD - Synchronous heavy work
function calculateStats(items: Item[]) {
// Heavy computation in loop
return items.map(item => ({
...item,
score: complexAlgorithm(item), // Blocking
}));
}
// ā
GOOD - Break into chunks with yielding
async function calculateStats(items: Item[]) {
const results = [];
const CHUNK_SIZE = 100;
for (let i = 0; i < items.length; i += CHUNK_SIZE) {
const chunk = items.slice(i, i + CHUNK_SIZE);
results.push(...chunk.map(item => ({
...item,
score: complexAlgorithm(item),
})));
// Yield to event loop every chunk
await new Promise(resolve => setTimeout(resolve, 0));
}
return results;
}
```
### 4. React Component Optimization
#### Avoid Expensive Operations in Render
```typescript
// ā BAD - Expensive operation every render
function TokenList({ tokens }: { tokens: Token[] }) {
const sorted = tokens.sort((a, b) =>
expensiveComparison(a, b) // Runs every render!
);
return <List data={sorted} />;
}
// ā
GOOD - Memoize expensive operations
function TokenList({ tokens }: { tokens: Token[] }) {
const sorted = useMemo(
() => tokens.sort((a, b) => expensiveComparison(a, b)),
[tokens],
);
return <List data={sorted} />;
}
```
#### Use memo for Heavy Components
```typescript
// ā BAD - Re-renders even when props unchanged
function HeavyComponent({ data }: { data: Data }) {
// Expensive rendering logic
return <ComplexUI data={data} />;
}
// ā
GOOD - Skip re-render if props unchanged
import { memo } from 'react';
const HeavyComponent = memo(function HeavyComponent({ data }: { data: Data }) {
return <ComplexUI data={data} />;
});
```
#### Stable Callbacks
```typescript
// ā BAD - New function every render
function Parent() {
return <Child onPress={() => handlePress()} />; // New ref every time
}
// ā
GOOD - Stable callback reference
function Parent() {
const handlePress = useCallback(() => {
// handler logic
}, []); // Stable reference
return <Child onPress={handlePress} />;
}
```
### 5. List Rendering Optimization
#### Use FlashList for Long Lists (React Native)
```typescript
// ā BAD - FlatList for 1000+ items
import { FlatList } from 'react-native';
<FlatList
data={thousandsOfItems}
renderItem={({ item }) => <Item data={item} />}
/>
// ā
GOOD - FlashList for better performance
import { FlashList } from '@shopify/flash-list';
<FlashList
data={thousandsOfItems}
renderItem={({ item }) => <Item data={item} />}
estimatedItemSize={80} // Important for performance
/>
```
#### Optimize windowSize for Memory Management (React Native)
**Problem**: Default `windowSize` (21) keeps too many items mounted, consuming memory on low-end devices.
**Solution**: Reduce `windowSize` to limit number of rendered items outside viewport.
```typescript
// ā BAD - Default windowSize = 21 (renders 21 pages of items)
<FlatList
data={items}
renderItem={renderItem}
// windowSize not specified - defaults to 21
/>
// ā
GOOD - Reduced windowSize for better memory usage
<FlatList
data={items}
renderItem={renderItem}
windowSize={5} // Renders 5 pages (2 above + current + 2 below)
/>
// ā
BETTER - Conditional based on platform and context
<FlatList
data={items}
renderItem={renderItem}
windowSize={platformEnv.isNativeAndroid && inTabList ? 3 : 5}
// Android in tabs: 3 (memory constrained)
// Other platforms: 5 (balanced)
/>
```
**How windowSize works**:
- `windowSize={5}` means: 5 Ć viewport height of items are mounted
- Example: If viewport shows 10 items:
- windowSize=5 ā 50 items mounted (20 above + 10 visible + 20 below)
- windowSize=3 ā 30 items mounted (10 above + 10 visible + 10 below)
- windowSize=21 (default) ā 210 items mounted
**When to use smaller windowSize**:
- Low-end devices (especially Android)
- Complex item components (heavy rendering)
- Lists inside tabs/nested scrollviews
- Memory-constrained scenarios
**Trade-offs**:
- ā
Lower memory usage
- ā
Better performance on low-end devices
- ā More frequent item mounting/unmounting during fast scrolling
- ā Potential blank space if scrolling very fast
---
**šØ Developer Alert: windowSize Optimization**
**ā
Using `ListView` from `@onekeyhq/components`?**
- ā
**Already optimized with `windowSize={5}` built-in**
- ā
**No need to set windowSize manually**
- ā ļø **ONLY override if you need a different value** (e.g., `windowSize={3}` for Android tabs)
**ā ļø Using `FlatList` or custom list component?**
- ā ļø **Set `windowSize={3-5}` manually** (default 21 is too large)
- Recommended: `windowSize={5}` (balanced)
- Memory-constrained: `windowSize={3}` (Android tabs, low-end devices)
---
**Example using OneKey ListView**:
```typescript
// ā
RECOMMENDED - Use OneKey's ListView (already optimized)
import { ListView } from '@onekeyhq/components';
<ListView
data={items}
renderItem={renderItem}
estimatedItemSize={80}
// windowSize={5} is already set internally - no need to specify!
/>
// ā ļø ONLY set windowSize when you need different value
<ListView
data={items}
renderItem={renderItem}
estimatedItemSize={80}
windowSize={3} // Override only for specific cases (e.g., Android tabs)
/>
```
#### Virtualization Keys
```typescript
// ā BAD - Index as key (causes re-renders)
{items.map((item, index) => (
<Item key={index} data={item} />
))}
// ā
GOOD - Stable unique key
{items.map((item) => (
<Item key={item.id} data={item} />
))}
```
#### Item Component Memoization
```typescript
// ā BAD - List item re-renders on parent update
function ListItem({ item }: { item: Item }) {
return <View>{item.name}</View>;
}
// ā
GOOD - Memoized list item
const ListItem = memo(function ListItem({ item }: { item: Item }) {
return <View>{item.name}</View>;
});
```
#### CSS content-visibility for Long Lists (Web Only)
**Problem**: Browser renders all DOM elements even if they're off-screen, causing slow initial render.
**Solution**: Use `content-visibility: auto` to defer off-screen rendering.
```css
/* Apply to list items */
.message-item {
content-visibility: auto;
contain-intrinsic-size: 0 80px; /* Estimated item height for layout calculations */
}
```
**Example**:
```tsx
// Web component with CSS optimization
function MessageList({ messages }: { messages: Message[] }) {
return (
<div className="overflow-y-auto h-screen">
{messages.map(msg => (
<div key={msg.id} className="message-item">
<Avatar user={msg.author} />
<div>{msg.content}</div>
</div>
))}
</div>
);
}
```
**Performance Impact**:
- For 1000 messages: Browser skips layout/paint for ~990 off-screen items
- **10Ć faster initial render** on long lists
- Rendering work happens lazily as user scrolls
**When to use**:
- Web applications with long scrollable lists
- Item height is relatively uniform
- 100+ items in the list
- Initial render performance is critical
**Important Notes**:
- ā
This is a **CSS-only** optimization for web platforms
- ā
For React Native, use `ListView` or `FlashList` instead
- ā
Browser support: Chrome/Edge 85+, Safari 16.4+
---
### šØ Developer Alert: Built-in Optimizations
**ā
Already Optimized - NO ACTION NEEDED:**
| Component | Optimization | What's Included |
|-----------|--------------|-----------------|
| `ListView` from `@onekeyhq/components` | `windowSize={5}` | Automatically limits visible items to 5Ć viewport height |
| `Tabs` from `@onekeyhq/components` | `contentVisibility: 'hidden'` | Inactive tabs auto-hidden, only focused tab visible |
| `Dialog` from `@onekeyhq/components` | `contentVisibility: 'hidden'` | Auto-hidden when closed (with `forceMount`) |
| Modal Navigators | `contentVisibility: 'hidden'` | Non-current routes auto-hidden |
| Market Views | `contentVisibility: 'hidden'` | Inactive market views auto-hidden |
**ā ļø MANUAL ACTION REQUIRED - Business Components:**
| Scenario | Action | Example |
|----------|--------|---------|
| Long scrollable lists (100+ items) | Add `content-visibility: auto` CSS to list items | Message list, transaction history, NFT gallery |
| Custom tab-like components | Add `contentVisibility: 'hidden'` manually | Not using `@onekeyhq/components/Tabs` |
| Custom visibility toggles | Add `contentVisibility: 'hidden'` manually | Collapsible panels, accordion items, show/hide sections |
| FlatList with heavy items | Set `windowSize={3-5}` manually | Product list, image gallery with complex items |
**Quick Decision Tree:**
```
Are you using OneKey base components?
āā YES (ListView, Tabs, Dialog) ā ā
Already optimized, do nothing
āā NO (Custom/business components)
āā Is it a long list (100+ items)?
āā YES ā ā ļø Add content-visibility: auto (Web) or windowSize (Native)
āā NO ā Is it tab-like or toggle-able content?
āā YES ā ā ļø Add contentVisibility: 'hidden'
āā NO ā No action needed
```
---
**OneKey Components with Built-in `contentVisibility`**:
The following OneKey components **already use** `contentVisibility: 'hidden'` to optimize inactive content:
1. **`Tabs` Component** (`@onekeyhq/components`):
- Automatically hides inactive tab content
- Uses `contentVisibility: 'hidden'` for non-focused tabs
- ā
**No need to add contentVisibility when using Tabs**
```typescript
// ā
Tabs component auto-optimizes with contentVisibility
import { Tabs } from '@onekeyhq/components';
<Tabs>
<Tabs.Tab name="tab1" label="Tab 1">
<HeavyContent /> {/* Auto-hidden when not focused */}
</Tabs.Tab>
<Tabs.Tab name="tab2" label="Tab 2">
<AnotherHeavyContent /> {/* Auto-hidden when not focused */}
</Tabs.Tab>
</Tabs>
// Internal: element.style.contentVisibility = isFocused ? 'visible' : 'hidden'
```
2. **Modal Navigators**:
- `createOnBoardingNavigator`: Hides non-current routes
- `createWebModalNavigator`: Hides non-current routes
- ā
**Automatically optimized**
```typescript
// Internal implementation for non-current routes:
// style={{ contentVisibility: !isCurrentRoute ? 'hidden' : undefined }}
```
3. **`Dialog` Component** (`@onekeyhq/components`):
- Uses `contentVisibility: 'hidden'` when closed with `forceMount`
- ā
**Automatically optimized**
```typescript
// ā
Dialog auto-hides when closed but force-mounted
<Dialog forceMount>
<Dialog.Content>
{/* Hidden with contentVisibility when dialog is closed */}
</Dialog.Content>
</Dialog>
```
4. **Market Views**:
- `MarketHomeV2/DesktopLayout`: Hides inactive market views
- ā
**Automatically optimized**
```typescript
// Market watchlist and normal list auto-hidden when not focused
// style={isNotFocused ? { contentVisibility: 'hidden' } : undefined}
```
**When to manually add `contentVisibility`**:
- ā ļø **Long scrollable lists** (100+ items) - use `content-visibility: auto` for list items
- ā ļø **Custom tab-like components** - not using `@onekeyhq/components/Tabs`
- ā ļø **Custom visibility toggles** - manually shown/hidden content
**How to apply for long lists**:
1. Create a CSS class with `content-visibility: auto`
2. Apply the class to your list item components
3. Set appropriate `contain-intrinsic-size` (estimated item height)
### 6. State Updates Optimization
#### Batch State Updates
```typescript
// ā BAD - Multiple state updates = multiple re-renders
function updateAll() {
setName('John'); // Re-render 1
setAge(30); // Re-render 2
setEmail('[email protected]'); // Re-render 3
}
// ā
GOOD - React 18+ auto-batches in event handlers
function updateAll() {
setName('John');
setAge(30);
setEmail('[email protected]');
// Single re-render (React 18+)
}
// ā
GOOD - Use single state object for related data
const [user, setUser] = useState({ name: '', age: 0, email: '' });
function updateAll() {
setUser({ name: 'John', age: 30, email: '[email protected]' });
// Single re-render
}
```
#### Avoid Derived State
```typescript
// ā BAD - Duplicating state
function UserProfile({ user }: { user: User }) {
const [name, setName] = useState(user.name); // Duplicate!
useEffect(() => {
setName(user.name); // Sync nightmare
}, [user.name]);
}
// ā
GOOD - Use props directly or derive during render
function UserProfile({ user }: { user: User }) {
const displayName = user.name.toUpperCase(); // Derive on render
return <Text>{displayName}</Text>;
}
```
### 7. Image Optimization
```typescript
// ā BAD - No size constraints
<Image source={{ uri: imageUrl }} />
// ā
GOOD - Specify dimensions
<Image
source={{ uri: imageUrl }}
style={{ width: 100, height: 100 }}
resizeMode="cover"
/>
// ā
BETTER - Use optimized image component
import { Image } from '@onekeyhq/components';
<Image
src={imageUrl}
width={100}
height={100}
// Auto-optimizes and caches
/>
```
### 8. Async Operation Patterns
#### Parallel vs Sequential
```typescript
// ā BAD - Sequential when parallel would work
const user = await fetchUser();
const settings = await fetchSettings();
const preferences = await fetchPreferences();
// Total: 300ms + 200ms + 150ms = 650ms
// ā
GOOD - Parallel independent requests
const [user, settings, preferences] = await Promise.all([
fetchUser(),
fetchSettings(),
fetchPreferences(),
]);
// Total: max(300ms, 200ms, 150ms) = 300ms
// ā ļø IMPORTANT - But limit concurrency for many requests!
// See Rule #1: Concurrent Request Control
```
#### Cancellation for Stale Requests
```typescript
// ā BAD - Race condition with stale data
function SearchInput() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
search(query).then(setResults); // Old results may arrive late!
}, [query]);
}
// ā
GOOD - Cancel stale requests
function SearchInput() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
const controller = new AbortController();
search(query, { signal: controller.signal })
.then(setResults)
.catch(err => {
if (err.name !== 'AbortError') throw err;
});
return () => controller.abort(); // Cancel on cleanup
}, [query]);
}
```
## Performance Measurement
### React DevTools Profiler
```typescript
// Wrap components to measure performance
import { Profiler } from 'react';
<Profiler
id="MyComponent"
onRender={(id, phase, actualDuration) => {
if (actualDuration > 16) { // Over 1 frame (60fps)
console.warn(`${id} slow render: ${actualDuration}ms`);
}
}}
>
<MyComponent />
</Profiler>
```
### React Native Performance Monitor
```typescript
// Enable in dev mode
import { PerformanceMonitor } from '@onekeyhq/shared/src/perf';
// Monitor specific operations
const measure = PerformanceMonitor.start('token-list-load');
await loadTokens();
measure.end(); // Logs if > threshold
```
## Performance Checklist
Before merging performance-critical code, verify:
- [ ] **Network requests**: Limited to 3-5 concurrent
- [ ] **Bridge crossings**: Minimized, data batched
- [ ] **Heavy operations**: Deferred with `InteractionManager` or `setTimeout`
- [ ] **List rendering**: Using `FlashList` for 100+ items (React Native) or `content-visibility` for long lists (Web)
- [ ] **Component memoization**: Heavy components wrapped with `memo`
- [ ] **Callbacks**: Stable with `useCallback` when passed to memoized children
- [ ] **Expensive computations**: Memoized with `useMemo`
- [ ] **State updates**: Batched, no derived state
- [ ] **Images**: Sized appropriately, using optimized component
- [ ] **Async operations**: Cancellable, avoid race conditions
## Real-World Example: iOS AppHang Fix
**Problem**: 15+ concurrent network requests caused 5-second UI freeze on iPhone 7
**Root Cause**:
```
15 concurrent requests
ā 90+ React Native bridge messages
ā Bridge saturation
ā Main thread blocked
ā iOS Watchdog kills app (AppHang)
```
**Solution**: Batched execution with concurrency limit
```typescript
// Before: All requests fire at once
const requests = accountAddressList.map(account =>
this.updateNetworkTokenList(account)
);
await Promise.all(requests); // š„ 15+ concurrent
// After: Batched with limit of 3
const tasks = accountAddressList.map(account =>
() => this.updateNetworkTokenList(account)
);
const results = await this.executeBatched(tasks, 3); // ā
Max 3 concurrent
```
**Result**:
- UI freeze eliminated
- Smooth navigation animations
- No more AppHang errors
- Better error handling with `Promise.allSettled`
**Lesson**: Always consider device capabilities and bridge limitations when designing concurrent operations.
## Performance Anti-Patterns
### 1. "It works on my device"
```typescript
// ā Your MacBook Pro can handle this, iPhone 7 cannot
await Promise.all(twentyRequests); // Works on M1, hangs on A10
```
**Solution**: Test on low-end devices (iPhone 7, Android mid-range)
### 2. "Premature optimization"
```typescript
// ā Over-memoizing simple components
const Button = memo(function Button({ label }: { label: string }) {
return <Text>{label}</Text>; // Too simple to benefit from memo
});
```
**Solution**: Profile first, optimize bottlenecks
### 3. "Memo everything"
```typescript
// ā Memoizing where it hurts performance
const expensiveMemoCheck = useMemo(
() => cheapOperation(),
[dep1, dep2, dep3, dep4, dep5], // Expensive dependency check!
);
```
**Solution**: Only memoize expensive operations (>10ms)
## Related Documentation
- [Promise Handling](./promise-handling.md) - Async patterns and error handling
- [React Components](./react-components.md) - Component structure and hooks
- [Error Handling](./error-handling.md) - Error boundaries and recovery
- [1k-dev-workflows: Fix Sentry Errors](../../1k-dev-workflows/references/rules/fix-sentry-errors.md) - Performance issue analysis
## External References
- [React Native Performance](https://reactnative.dev/docs/performance)
- [React Profiler API](https://react.dev/reference/react/Profiler)
- [FlashList Documentation](https://shopify.github.io/flash-list/)
```