ReactNative react-native

React Native是一种用于开发跨平台移动应用的框架,它允许开发者使用JavaScript和React来构建原生应用。这个技能涵盖了React Native的项目结构、组件模式、状态管理、测试和平台特定代码的最佳实践。

移动开发 0 次安装 0 次浏览 更新于 3/5/2026

React Native 技能

加载方式:base.md + typescript.md

项目结构

project/
├── src/
│   ├── core/                   # 纯业务逻辑(无React)
│   │   ├── types.ts
│   │   └── services/
│   ├── components/             # 可重用的UI组件
│   │   ├── Button/
│   │   │   ├── Button.tsx
│   │   │   ├── Button.test.tsx
│   │   │   └── index.ts
│   │   └── index.ts            # 桶导出
│   ├── screens/                # 屏幕组件
│   │   ├── Home/
│   │   │   ├── HomeScreen.tsx
│   │   │   ├── useHome.ts      # 屏幕特定钩子
│   │   │   └── index.ts
│   │   └── index.ts
│   ├── navigation/             # 导航配置
│   ├── hooks/                  # 共享自定义钩子
│   ├── store/                  # 状态管理
│   └── utils/                  # 工具
├── __tests__/
├── android/
├── ios/
└── CLAUDE.md

组件模式

仅使用函数组件

// 好的 - 简单,可测试
interface ButtonProps {
  label: string;
  onPress: () => void;
  disabled?: boolean;
}

export function Button({ label, onPress, disabled = false }: ButtonProps): JSX.Element {
  return (
    <Pressable onPress={onPress} disabled={disabled}>
      <Text>{label}</Text>
    </Pressable>
  );
}

将逻辑提取到钩子

// useHome.ts - 所有逻辑都在这里
export function useHome() {
  const [items, setItems] = useState<Item[]>([]);
  const [loading, setLoading] = useState(false);

  const refresh = useCallback(async () => {
    setLoading(true);
    const data = await fetchItems();
    setItems(data);
    setLoading(false);
  }, []);

  return { items, loading, refresh };
}

// HomeScreen.tsx - 纯展示
export function HomeScreen(): JSX.Element {
  const { items, loading, refresh } = useHome();
  
  return (
    <ItemList items={items} loading={loading} onRefresh={refresh} />
  );
}

总是明确Props接口

// 总是定义props接口,即使简单
interface ItemCardProps {
  item: Item;
  onPress: (id: string) => void;
}

export function ItemCard({ item, onPress }: ItemCardProps): JSX.Element {
  ...
}

状态管理

首先使用本地状态

// 从useState开始,只有在需要时才升级
const [value, setValue] = useState('');

如果需要,使用Zustand进行全局状态管理

// store/useAppStore.ts
import { create } from 'zustand';

interface AppState {
  user: User | null;
  setUser: (user: User | null) => void;
}

export const useAppStore = create<AppState>((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

使用React Query进行服务器状态管理

// hooks/useItems.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

export function useItems() {
  return useQuery({
    queryKey: ['items'],
    queryFn: fetchItems,
  });
}

export function useCreateItem() {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: createItem,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['items'] });
    },
  });
}

测试

使用React Native Testing Library进行组件测试

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

describe('Button', () => {
  it('按下时调用onPress', () => {
    const onPress = jest.fn();
    const { getByText } = render(<Button label="点击我" onPress={onPress} />);
    
    fireEvent.press(getByText('点击我'));
    
    expect(onPress).toHaveBeenCalledTimes(1);
  });

  it('禁用时不调用onPress', () => {
    const onPress = jest.fn();
    const { getByText } = render(<Button label="点击我" onPress={onPress} disabled />);
    
    fireEvent.press(getByText('点击我'));
    
    expect(onPress).not.toHaveBeenCalled();
  });
});

钩子测试

import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter';

describe('useCounter', () => {
  it('增加计数器', () => {
    const { result } = renderHook(() => useCounter());
    
    act(() => {
      result.current.increment();
    });
    
    expect(result.current.count).toBe(1);
  });
});

平台特定代码

谨慎使用Platform.select

import { Platform } from 'react-native';

const styles = StyleSheet.create({
  shadow: Platform.select({
    ios: {
      shadowColor: '#000',
      shadowOffset: { width: 0, height: 2 },
      shadowOpacity: 0.1,
    },
    android: {
      elevation: 2,
    },
  }),
});

对于复杂差异,使用单独的文件

Component/
├── Component.tsx          # 共享逻辑
├── Component.ios.tsx      # iOS特定
├── Component.android.tsx  # Android特定
└── index.ts

React Native 反模式

  • ❌ 内联样式 - 使用StyleSheet.create
  • ❌ 渲染中的逻辑 - 提取到钩子
  • ❌ 深层组件嵌套 - 扁平层次结构
  • ❌ 匿名函数作为props - 使用useCallback
  • ❌ 列表中使用索引作为key - 使用稳定的ID
  • ❌ 直接状态突变 - 总是使用setter
  • ❌ 将业务逻辑与UI混合 - 保持core/纯粹
  • ❌ 忽略TypeScript错误 - 修复它们
  • ❌ 大型组件 - 分割成更小的部分