react-native-expert

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Native Expert Skill

React Native专家技能

Expert-level React Native patterns, mobile-specific optimizations, navigation, and platform handling.

专家级React Native模式、移动端特定优化、导航和平台适配方案。

Auto-Detection

自动触发场景

This skill activates when:
  • Working with React Native projects
  • Detected
    react-native
    or
    expo
    in package.json
  • Building mobile components
  • Platform-specific code needed

当以下情况时,该技能会激活:
  • 处理React Native项目时
  • 在package.json中检测到
    react-native
    expo
  • 构建移动端组件时
  • 需要平台特定代码时

1. Project Structure

1. 项目结构

Recommended Structure

推荐的项目结构

src/
├── components/           # Shared components
│   ├── ui/              # Base UI components
│   └── common/          # Business components
├── screens/             # Screen components
├── navigation/          # Navigation config
├── hooks/               # Custom hooks
├── services/            # API services
├── stores/              # State management
├── utils/               # Utilities
├── constants/           # App constants
├── types/               # TypeScript types
└── assets/              # Images, fonts

src/
├── components/           # 通用组件
│   ├── ui/              # 基础UI组件
│   └── common/          # 业务组件
├── screens/             # 页面组件
├── navigation/          # 导航配置
├── hooks/               # 自定义hooks
├── services/            # API服务
├── stores/              # 状态管理
├── utils/               # 工具函数
├── constants/           # 应用常量
├── types/               # TypeScript类型定义
└── assets/              # 图片、字体等资源

2. Component Patterns

2. 组件模式

Adaptive Styling Detection

自适应样式检测

typescript
// ✅ GOOD - Detect and use project's styling approach
// Check package.json for: nativewind, emotion, styled-components, or use StyleSheet

// NativeWind (Tailwind)
import { styled } from 'nativewind';
const StyledView = styled(View);
<StyledView className="flex-1 bg-white p-4" />

// StyleSheet (default)
const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: 'white', padding: 16 },
});
<View style={styles.container} />

// Emotion
import styled from '@emotion/native';
const Container = styled.View`flex: 1; background-color: white;`;
typescript
// ✅ 推荐 - 检测并适配项目的样式方案
// 检查package.json中是否包含:nativewind、emotion、styled-components,或使用StyleSheet

// NativeWind(Tailwind风格)
import { styled } from 'nativewind';
const StyledView = styled(View);
<StyledView className="flex-1 bg-white p-4" />

// StyleSheet(默认方案)
const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: 'white', padding: 16 },
});
<View style={styles.container} />

// Emotion
import styled from '@emotion/native';
const Container = styled.View`flex: 1; background-color: white;`;

Performance-Optimized Components

性能优化组件

tsx
// ✅ GOOD - Memoized list item
const ListItem = React.memo(function ListItem({ item, onPress }: Props) {
  const handlePress = useCallback(() => {
    onPress(item.id);
  }, [item.id, onPress]);

  return (
    <Pressable onPress={handlePress}>
      <Text>{item.title}</Text>
    </Pressable>
  );
});

// ✅ GOOD - Pressable over TouchableOpacity
<Pressable
  onPress={handlePress}
  style={({ pressed }) => [
    styles.button,
    pressed && styles.buttonPressed
  ]}
  android_ripple={{ color: 'rgba(0,0,0,0.1)' }}
>
  <Text>Press Me</Text>
</Pressable>

tsx
// ✅ 推荐 - 记忆化列表项组件
const ListItem = React.memo(function ListItem({ item, onPress }: Props) {
  const handlePress = useCallback(() => {
    onPress(item.id);
  }, [item.id, onPress]);

  return (
    <Pressable onPress={handlePress}>
      <Text>{item.title}</Text>
    </Pressable>
  );
});

// ✅ 推荐 - 使用Pressable替代TouchableOpacity
<Pressable
  onPress={handlePress}
  style={({ pressed }) => [
    styles.button,
    pressed && styles.buttonPressed
  ]}
  android_ripple={{ color: 'rgba(0,0,0,0.1)' }}
>
  <Text>Press Me</Text>
</Pressable>

3. FlatList Optimization

3. FlatList优化

Required Optimizations

必备优化项

tsx
// ✅ GOOD - Optimized FlatList
<FlatList
  data={items}
  renderItem={renderItem}
  keyExtractor={keyExtractor}
  // Performance props
  removeClippedSubviews={true}
  maxToRenderPerBatch={10}
  windowSize={5}
  initialNumToRender={10}
  getItemLayout={getItemLayout} // If fixed height
  // Memoized functions
  ListEmptyComponent={EmptyComponent}
  ListHeaderComponent={HeaderComponent}
  ListFooterComponent={FooterComponent}
  // Prevent re-renders
  extraData={selectedId}
/>

// ✅ GOOD - Memoized renderItem
const renderItem = useCallback(({ item }: { item: Item }) => (
  <ListItem item={item} onPress={handlePress} />
), [handlePress]);

// ✅ GOOD - Stable keyExtractor
const keyExtractor = useCallback((item: Item) => item.id, []);

// ✅ GOOD - getItemLayout for fixed height items
const getItemLayout = useCallback((
  _data: Item[] | null | undefined,
  index: number
) => ({
  length: ITEM_HEIGHT,
  offset: ITEM_HEIGHT * index,
  index,
}), []);
tsx
// ✅ 推荐 - 优化后的FlatList
<FlatList
  data={items}
  renderItem={renderItem}
  keyExtractor={keyExtractor}
  // 性能相关属性
  removeClippedSubviews={true}
  maxToRenderPerBatch={10}
  windowSize={5}
  initialNumToRender={10}
  getItemLayout={getItemLayout} // 固定高度时使用
  // 记忆化函数
  ListEmptyComponent={EmptyComponent}
  ListHeaderComponent={HeaderComponent}
  ListFooterComponent={FooterComponent}
  // 防止不必要重渲染
  extraData={selectedId}
/>

// ✅ 推荐 - 记忆化renderItem函数
const renderItem = useCallback(({ item }: { item: Item }) => (
  <ListItem item={item} onPress={handlePress} />
), [handlePress]);

// ✅ 推荐 - 稳定的keyExtractor函数
const keyExtractor = useCallback((item: Item) => item.id, []);

// ✅ 推荐 - 固定高度列表使用getItemLayout
const getItemLayout = useCallback((
  _data: Item[] | null | undefined,
  index: number
) => ({
  length: ITEM_HEIGHT,
  offset: ITEM_HEIGHT * index,
  index,
}), []);

FlashList Alternative

FlashList替代方案

tsx
// ✅ BETTER - Use FlashList for large lists
import { FlashList } from '@shopify/flash-list';

<FlashList
  data={items}
  renderItem={renderItem}
  estimatedItemSize={ITEM_HEIGHT}
  keyExtractor={keyExtractor}
/>

tsx
// ✅ 更优 - 大数据列表使用FlashList
import { FlashList } from '@shopify/flash-list';

<FlashList
  data={items}
  renderItem={renderItem}
  estimatedItemSize={ITEM_HEIGHT}
  keyExtractor={keyExtractor}
/>

4. Navigation (React Navigation)

4. 导航(React Navigation)

Type-Safe Navigation

类型安全导航

tsx
// ✅ GOOD - Define navigation types
type RootStackParamList = {
  Home: undefined;
  Profile: { userId: string };
  Settings: { section?: string };
};

type ProfileScreenProps = NativeStackScreenProps<RootStackParamList, 'Profile'>;

// ✅ GOOD - Type-safe navigation hook
import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';

type NavigationProp = NativeStackNavigationProp<RootStackParamList>;

function MyComponent() {
  const navigation = useNavigation<NavigationProp>();

  const goToProfile = (userId: string) => {
    navigation.navigate('Profile', { userId });
  };
}
tsx
// ✅ 推荐 - 定义导航类型
 type RootStackParamList = {
  Home: undefined;
  Profile: { userId: string };
  Settings: { section?: string };
};

type ProfileScreenProps = NativeStackScreenProps<RootStackParamList, 'Profile'>;

// ✅ 推荐 - 类型安全的导航Hook
import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';

type NavigationProp = NativeStackNavigationProp<RootStackParamList>;

function MyComponent() {
  const navigation = useNavigation<NavigationProp>();

  const goToProfile = (userId: string) => {
    navigation.navigate('Profile', { userId });
  };
}

Deep Linking

深度链接

tsx
// ✅ GOOD - Configure deep linking
const linking = {
  prefixes: ['myapp://', 'https://myapp.com'],
  config: {
    screens: {
      Home: '',
      Profile: 'user/:userId',
      Settings: 'settings',
    },
  },
};

<NavigationContainer linking={linking}>
  {/* ... */}
</NavigationContainer>

tsx
// ✅ 推荐 - 配置深度链接
const linking = {
  prefixes: ['myapp://', 'https://myapp.com'],
  config: {
    screens: {
      Home: '',
      Profile: 'user/:userId',
      Settings: 'settings',
    },
  },
};

<NavigationContainer linking={linking}>
  {/* ... */}
</NavigationContainer>

5. Platform-Specific Code

5. 平台特定代码

Platform Selection

平台选择

tsx
import { Platform, StyleSheet } from 'react-native';

// ✅ GOOD - Platform.select
const styles = StyleSheet.create({
  shadow: Platform.select({
    ios: {
      shadowColor: '#000',
      shadowOffset: { width: 0, height: 2 },
      shadowOpacity: 0.25,
      shadowRadius: 3.84,
    },
    android: {
      elevation: 5,
    },
    default: {},
  }),
});

// ✅ GOOD - Platform-specific files
// Button.ios.tsx
// Button.android.tsx
// Button.tsx (fallback)
import Button from './Button'; // Auto-selects platform
tsx
import { Platform, StyleSheet } from 'react-native';

// ✅ 推荐 - 使用Platform.select
const styles = StyleSheet.create({
  shadow: Platform.select({
    ios: {
      shadowColor: '#000',
      shadowOffset: { width: 0, height: 2 },
      shadowOpacity: 0.25,
      shadowRadius: 3.84,
    },
    android: {
      elevation: 5,
    },
    default: {},
  }),
});

// ✅ 推荐 - 平台特定文件
// Button.ios.tsx
// Button.android.tsx
// Button.tsx( fallback )
import Button from './Button'; // 自动适配平台

Safe Area Handling

安全区域适配

tsx
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';

// ✅ GOOD - SafeAreaView for screens
function Screen() {
  return (
    <SafeAreaView style={{ flex: 1 }} edges={['top', 'bottom']}>
      <Content />
    </SafeAreaView>
  );
}

// ✅ GOOD - useSafeAreaInsets for custom handling
function CustomHeader() {
  const insets = useSafeAreaInsets();
  return (
    <View style={{ paddingTop: insets.top }}>
      <HeaderContent />
    </View>
  );
}

tsx
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';

// ✅ 推荐 - 页面使用SafeAreaView
function Screen() {
  return (
    <SafeAreaView style={{ flex: 1 }} edges={['top', 'bottom']}>
      <Content />
    </SafeAreaView>
  );
}

// ✅ 推荐 - 自定义场景使用useSafeAreaInsets
function CustomHeader() {
  const insets = useSafeAreaInsets();
  return (
    <View style={{ paddingTop: insets.top }}>
      <HeaderContent />
    </View>
  );
}

6. Image Handling

6. 图片处理

Optimized Images

优化的图片处理方案

tsx
import FastImage from 'react-native-fast-image';

// ✅ GOOD - Use FastImage for network images
<FastImage
  source={{
    uri: imageUrl,
    priority: FastImage.priority.normal,
    cache: FastImage.cacheControl.immutable,
  }}
  style={styles.image}
  resizeMode={FastImage.resizeMode.cover}
/>

// ✅ GOOD - Preload images
FastImage.preload([
  { uri: 'https://example.com/image1.jpg' },
  { uri: 'https://example.com/image2.jpg' },
]);

// ✅ GOOD - Local images with require
<Image source={require('./assets/logo.png')} />

tsx
import FastImage from 'react-native-fast-image';

// ✅ 推荐 - 网络图片使用FastImage
<FastImage
  source={{
    uri: imageUrl,
    priority: FastImage.priority.normal,
    cache: FastImage.cacheControl.immutable,
  }}
  style={styles.image}
  resizeMode={FastImage.resizeMode.cover}
/>

// ✅ 推荐 - 预加载图片
FastImage.preload([
  { uri: 'https://example.com/image1.jpg' },
  { uri: 'https://example.com/image2.jpg' },
]);

// ✅ 推荐 - 本地图片使用require
<Image source={require('./assets/logo.png')} />

7. Animations

7. 动画

Reanimated Best Practices

Reanimated最佳实践

tsx
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
  withTiming,
  runOnJS,
} from 'react-native-reanimated';

// ✅ GOOD - Worklet-based animations
function AnimatedCard() {
  const scale = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  const handlePressIn = () => {
    scale.value = withSpring(0.95);
  };

  const handlePressOut = () => {
    scale.value = withSpring(1);
  };

  return (
    <Pressable onPressIn={handlePressIn} onPressOut={handlePressOut}>
      <Animated.View style={[styles.card, animatedStyle]}>
        <Content />
      </Animated.View>
    </Pressable>
  );
}
tsx
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
  withTiming,
  runOnJS,
} from 'react-native-reanimated';

// ✅ 推荐 - 基于Worklet的动画
function AnimatedCard() {
  const scale = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  const handlePressIn = () => {
    scale.value = withSpring(0.95);
  };

  const handlePressOut = () => {
    scale.value = withSpring(1);
  };

  return (
    <Pressable onPressIn={handlePressIn} onPressOut={handlePressOut}>
      <Animated.View style={[styles.card, animatedStyle]}>
        <Content />
      </Animated.View>
    </Pressable>
  );
}

Gesture Handler

手势处理

tsx
import { Gesture, GestureDetector } from 'react-native-gesture-handler';

// ✅ GOOD - Gesture-based interactions
function SwipeableCard() {
  const translateX = useSharedValue(0);

  const gesture = Gesture.Pan()
    .onUpdate((e) => {
      translateX.value = e.translationX;
    })
    .onEnd(() => {
      translateX.value = withSpring(0);
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: translateX.value }],
  }));

  return (
    <GestureDetector gesture={gesture}>
      <Animated.View style={animatedStyle}>
        <Card />
      </Animated.View>
    </GestureDetector>
  );
}

tsx
import { Gesture, GestureDetector } from 'react-native-gesture-handler';

// ✅ 推荐 - 基于手势的交互
function SwipeableCard() {
  const translateX = useSharedValue(0);

  const gesture = Gesture.Pan()
    .onUpdate((e) => {
      translateX.value = e.translationX;
    })
    .onEnd(() => {
      translateX.value = withSpring(0);
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: translateX.value }],
  }));

  return (
    <GestureDetector gesture={gesture}>
      <Animated.View style={animatedStyle}>
        <Card />
      </Animated.View>
    </GestureDetector>
  );
}

8. Storage & Persistence

8. 存储与持久化

tsx
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';

// ✅ GOOD - AsyncStorage for non-sensitive data
async function saveSettings(settings: Settings) {
  await AsyncStorage.setItem('settings', JSON.stringify(settings));
}

// ✅ GOOD - SecureStore for sensitive data
async function saveToken(token: string) {
  await SecureStore.setItemAsync('authToken', token);
}

// ✅ GOOD - MMKV for performance-critical storage
import { MMKV } from 'react-native-mmkv';

const storage = new MMKV();
storage.set('user.name', 'John');
const name = storage.getString('user.name');

tsx
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';

// ✅ 推荐 - 非敏感数据使用AsyncStorage
async function saveSettings(settings: Settings) {
  await AsyncStorage.setItem('settings', JSON.stringify(settings));
}

// ✅ 推荐 - 敏感数据使用SecureStore
async function saveToken(token: string) {
  await SecureStore.setItemAsync('authToken', token);
}

// ✅ 推荐 - 性能敏感场景使用MMKV
import { MMKV } from 'react-native-mmkv';

const storage = new MMKV();
storage.set('user.name', 'John');
const name = storage.getString('user.name');

9. Testing

9. 测试

tsx
import { render, fireEvent, waitFor } from '@testing-library/react-native';

// ✅ GOOD - Component testing
describe('LoginButton', () => {
  it('calls onPress when pressed', () => {
    const onPress = jest.fn();
    const { getByText } = render(<LoginButton onPress={onPress} />);

    fireEvent.press(getByText('Login'));
    expect(onPress).toHaveBeenCalled();
  });
});

// ✅ GOOD - Async testing
it('shows loading then content', async () => {
  const { getByTestId, queryByTestId } = render(<DataScreen />);

  expect(getByTestId('loading')).toBeTruthy();

  await waitFor(() => {
    expect(queryByTestId('loading')).toBeNull();
    expect(getByTestId('content')).toBeTruthy();
  });
});

tsx
import { render, fireEvent, waitFor } from '@testing-library/react-native';

// ✅ 推荐 - 组件测试
describe('LoginButton', () => {
  it('点击时触发onPress', () => {
    const onPress = jest.fn();
    const { getByText } = render(<LoginButton onPress={onPress} />);

    fireEvent.press(getByText('Login'));
    expect(onPress).toHaveBeenCalled();
  });
});

// ✅ 推荐 - 异步测试
it('先显示加载状态再显示内容', async () => {
  const { getByTestId, queryByTestId } = render(<DataScreen />);

  expect(getByTestId('loading')).toBeTruthy();

  await waitFor(() => {
    expect(queryByTestId('loading')).toBeNull();
    expect(getByTestId('content')).toBeTruthy();
  });
});

Quick Reference

快速参考

toon
checklist[12]{area,best_practice}:
  Lists,Use FlashList or optimized FlatList
  Images,Use FastImage for network images
  Navigation,Type-safe with RootStackParamList
  Styling,Detect project approach (NativeWind/StyleSheet)
  Platform,Use Platform.select for differences
  Safe area,Use SafeAreaView or useSafeAreaInsets
  Animations,Use Reanimated for 60fps
  Gestures,Use Gesture Handler
  Storage,SecureStore for tokens AsyncStorage for prefs
  Performance,Memoize components and callbacks
  Testing,React Native Testing Library
  Pressable,Use over TouchableOpacity

Version: 1.3.0
toon
checklist[12]{area,best_practice}:
  Lists,Use FlashList or optimized FlatList
  Images,Use FastImage for network images
  Navigation,Type-safe with RootStackParamList
  Styling,Detect project approach (NativeWind/StyleSheet)
  Platform,Use Platform.select for differences
  Safe area,Use SafeAreaView or useSafeAreaInsets
  Animations,Use Reanimated for 60fps
  Gestures,Use Gesture Handler
  Storage,SecureStore for tokens AsyncStorage for prefs
  Performance,Memoize components and callbacks
  Testing,React Native Testing Library
  Pressable,Use over TouchableOpacity

版本: 1.3.0