React Native

alinaqi/maggy/skills/react-native

作者 alinaqi72a456e6ccf0無授權條款707 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫2 週前更新

React Native mobile patterns, platform-specific code

AI 產生的概覽

React Native 編碼慣例,涵蓋專案結構、元件、狀態管理、測試與平台特定程式碼。

功能
提供 React Native 開發指引:建議的專案資料夾結構、函式元件與 Hook 模式、明確的 props 介面,以及本機狀態、Zustand、React Query 等狀態管理選擇。也涵蓋使用 React Native Testing Library 進行元件與 Hook 測試、透過 Platform.select 或獨立檔案處理平台差異,並列出應避免的反模式。
適用情境
適用於撰寫或審查 React Native 行動應用程式碼時,尤其是在決定專案結構、元件與 Hook 模式、狀態管理、測試方式或 iOS/Android 差異處理時。
執行需求
不附帶任何指令碼或工具,僅為說明性指引。內容假定專案使用 TypeScript 的 React Native 專案,並提及 Zustand、React Query 與 React Native Testing Library 等函式庫。

React Native Skill


Project Structure

project/├── src/│   ├── core/                   # Pure business logic (no React)│   │   ├── types.ts│   │   └── services/│   ├── components/             # Reusable UI components│   │   ├── Button/│   │   │   ├── Button.tsx│   │   │   ├── Button.test.tsx│   │   │   └── index.ts│   │   └── index.ts            # Barrel export│   ├── screens/                # Screen components│   │   ├── Home/│   │   │   ├── HomeScreen.tsx│   │   │   ├── useHome.ts      # Screen-specific hook│   │   │   └── index.ts│   │   └── index.ts│   ├── navigation/             # Navigation configuration│   ├── hooks/                  # Shared custom hooks│   ├── store/                  # State management│   └── utils/                  # Utilities├── __tests__/├── android/├── ios/└── CLAUDE.md

Component Patterns

Functional Components Only

typescript
// Good - simple, testableinterface 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>  );}

Extract Logic to Hooks

typescript
// useHome.ts - all logic hereexport 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 - pure presentationexport function HomeScreen(): JSX.Element {  const { items, loading, refresh } = useHome();    return (    <ItemList items={items} loading={loading} onRefresh={refresh} />  );}

Props Interface Always Explicit

typescript
// Always define props interface, even if simpleinterface ItemCardProps {  item: Item;  onPress: (id: string) => void;}
export function ItemCard({ item, onPress }: ItemCardProps): JSX.Element {  ...}

State Management

Local State First

typescript
// Start with useState, escalate only when neededconst [value, setValue] = useState('');

Zustand for Global State (if needed)

typescript
// store/useAppStore.tsimport { 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 for Server State

typescript
// hooks/useItems.tsimport { 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'] });    },  });}

Testing

Component Testing with React Native Testing Library

typescript
import { render, fireEvent } from '@testing-library/react-native';import { Button } from './Button';
describe('Button', () => {  it('calls onPress when pressed', () => {    const onPress = jest.fn();    const { getByText } = render(<Button label="Click me" onPress={onPress} />);        fireEvent.press(getByText('Click me'));        expect(onPress).toHaveBeenCalledTimes(1);  });
  it('does not call onPress when disabled', () => {    const onPress = jest.fn();    const { getByText } = render(<Button label="Click me" onPress={onPress} disabled />);        fireEvent.press(getByText('Click me'));        expect(onPress).not.toHaveBeenCalled();  });});

Hook Testing

typescript
import { renderHook, act } from '@testing-library/react-hooks';import { useCounter } from './useCounter';
describe('useCounter', () => {  it('increments counter', () => {    const { result } = renderHook(() => useCounter());        act(() => {      result.current.increment();    });        expect(result.current.count).toBe(1);  });});

Platform-Specific Code

Use Platform.select Sparingly

typescript
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,    },  }),});

Separate Files for Complex Differences

Component/├── Component.tsx          # Shared logic├── Component.ios.tsx      # iOS-specific├── Component.android.tsx  # Android-specific└── index.ts

React Native Anti-Patterns

  • ❌ Inline styles - use StyleSheet.create
  • ❌ Logic in render - extract to hooks
  • ❌ Deep component nesting - flatten hierarchy
  • ❌ Anonymous functions in props - use useCallback
  • ❌ Index as key in lists - use stable IDs
  • ❌ Direct state mutation - always use setter
  • ❌ Mixing business logic with UI - keep core/ pure
  • ❌ Ignoring TypeScript errors - fix them
  • ❌ Large components - split into smaller pieces

來源與署名

來源:alinaqi/maggy位於skills/react-native提交72a456e

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架