Back to skills
SkillHub ClubBuild MobileFull StackFrontendMobile

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.

Stars
2,326
Hot score
99
Updated
March 20, 2026
Overall rating
C5.0
Composite score
5.0
Best-practice grade
B73.6

Install command

npx @skill-hub/cli install onekeyhq-app-monorepo-1k-performance

Repository

OneKeyHQ/app-monorepo

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 repository

Best 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

Claude CodeCodex CLIGemini CLIOpenCode

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/)

```