Communitygithub.com

react-native-patterns

React Native 和 Expo 應用模式 — Expo Router 導航、狀態分離(伺服器/客戶端/路由/表單)、搭配 Zod 的 TanStack Query 資料擷取、高效列表、NativeWind/StyleSheet 樣式、原生 API 和安全儲存。在構建或編輯 React Native / Expo 螢幕、元件、導航或資料層時使用。

react-native-patterns 是什麼?

此技能提供一套實用模式,用於使用 Expo 構建生產級 React Native 應用程式。它涵蓋導航、狀態管理、資料擷取、列表渲染、樣式化和原生 API 存取,並緊密整合 Expo 生態系統,如 Expo Router 和 EAS。其核心原則包括:透過 Zod 驗證所有外部輸入(例如 API 回應和路由參數)、使用 TanStack Query 等伺服器快取庫區分伺服器狀態與用戶端狀態、優先使用 FlatList 或 FlashList 進行虛擬化列表、以及為樣式選擇單一一致系統(NativeWind 或 StyleSheet)。此外,它還強調安全最佳實務,例如使用 expo-secure-store 儲存令牌、在鉤子中封裝原生 API 呼叫並正確清理副作用,以及避免常見的反模式,例如在 ScrollView 中對大型陣列進行 map、將伺服器資料複製到用戶端儲存庫或信任未驗證的深度連結參數。此技能旨在與 `rules/react-native/` 規則集搭配使用,為開發者提供實際的實作方法,僅在明確涉及 React Native / Expo 螢幕、元件、導航或資料層的任務中使用。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/react-native-patterns

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

React Native / Expo 模式

用於使用 Expo 構建生產級 React Native 應用程式的實用模式。涵蓋導航、狀態、資料擷取、列表、樣式和原生 API。與 rules/react-native/ 規則集搭配使用:規則說明要強制執行什麼,此技能展示如何做。

下面提到的庫(NativeWind、Zustand/Jotai、TanStack Query)是常見且成熟的選項,僅作為示例說明 — 模式比特定套件更重要,任何等效套件都適用。使用 Zod 進行驗證,以與 ECC 現有的 typescript/ 規則保持一致。

這些模式假設在新架構(最近 Expo SDK 中的預設值,從 SDK 55+ 起為強制要求)上使用託管 Expo 工作流程(Expo Router、EAS、expo-* 模組)。它們不假設瀏覽器 DOM — React Native 沒有 <div>、沒有 URL 列,也沒有 Web 資料擷取預設值。

何時啟用

在以下情況下使用此技能:

  • 構建或編輯 React Native / Expo 螢幕、元件或導航
  • 使用 Expo Router(基於檔案的 app/ 目錄)設定路由
  • 決定狀態歸屬(伺服器快取與用戶端儲存庫與路由參數與表單)
  • 使用 TanStack Query 連接資料擷取並使用 Zod 驗證回應
  • 渲染長列表或重型列表
  • 選擇或應用樣式方法(NativeWind 或 StyleSheet)
  • 存取原生裝置 API(相機、位置、通知)或安全儲存
  • 審查 RN 程式碼中的行動裝置特定問題

不要在此處使用 Web/React-DOM 模式 — URL-as-state、<div> 和 SWR-for-browser 不適用於 React Native。

核心概念

專案結構(Expo Router)

基於檔案的路由,位於 app/ 下。保持路由檔案精簡:它們讀取並驗證參數,然後委派給位於 components/ 或 features/ 中的螢幕元件。

app/
  _layout.tsx          # 根堆疊
  (tabs)/
    _layout.tsx        # 標籤導航器
    index.tsx          # 首頁
  user/[id].tsx        # 動態路由
components/
features/
  user/UserProfile.tsx

導航:驗證路由參數

深度連結和動態路由會傳遞不可信的字串。在使用前使用 Zod 對其進行驗證。

// app/user/[id].tsx
import { useLocalSearchParams, router } from 'expo-router'
import { z } from 'zod'
import { UserProfile } from '@/features/user/UserProfile'

const Params = z.object({ id: z.string().uuid() })

export default function UserRoute() {
  const parsed = Params.safeParse(useLocalSearchParams())
  if (!parsed.success) {
    router.replace('/not-found')
    return null
  }
  return <UserProfile userId={parsed.data.id} />
}

狀態:保持關注點分離

不要將伺服器資料複製到用戶端儲存庫。每個關注點都有自己的歸宿。

關注點常見選擇
伺服器狀態(遠端資料)伺服器快取庫(TanStack Query、SWR)
用戶端/UI 狀態輕量級儲存庫(Zustand、Jotai)或 Context
路由/導航狀態Expo Router 參數
表單狀態表單庫(例如 React Hook Form)+ 結構描述驗證
秘密/令牌expo-secure-store
非秘密持久化AsyncStorage / MMKV

偏好使用本機 useState,直到狀態真正需要共用為止。

資料擷取:快取庫 + Zod

使用伺服器快取庫(TanStack Query、SWR)而不是 fetch-in-useEffect。在邊界處進行驗證,並從結構描述推斷類型。明確處理載入、錯誤和空狀態。(範例使用 TanStack Query。)

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { z } from 'zod'

const User = z.object({ id: z.string(), email: z.string().email() })
type User = z.infer<typeof User>

export function useUser(id: string) {
  return useQuery({
    queryKey: ['user', id],
    queryFn: async (): Promise<User> => User.parse(await api.getUser(id)),
  })
}

export function useUpdateEmail(id: string) {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (email: string) => api.updateEmail(id, email),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['user', id] }),
  })
}

列表:虛擬化,切勿在 ScrollView 中對大型陣列進行 map

import { FlatList } from 'react-native'

<FlatList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}          // 已記憶化
  initialNumToRender={10}
  windowSize={5}
/>

對於大型或異質列表,請使用 FlashList(Shopify)。

樣式:選擇一種系統

StyleSheet.create() 是框架原生的選項;工具類別庫(例如 NativeWind)是常見的替代方案。選擇一個並保持一致。切勿在熱路徑上的 JSX 中內聯建立樣式物件。

// NativeWind
<View className="p-4 rounded-2xl bg-white">
  <Text className="text-base font-semibold">Hello</Text>
</View>

// StyleSheet
const styles = StyleSheet.create({ card: { padding: 16, borderRadius: 16, backgroundColor: '#fff' } })
<View style={styles.card}>...</View>

原生 API:包裝在鉤子中,清理副作用

將 Expo SDK 呼叫和訂閱保留在 use* 鉤子內部,而不是 JSX 中。始終進行清理。

import { useEffect, useState } from 'react'
import * as Location from 'expo-location'

type LocationState =
  | { status: 'loading' }
  | { status: 'denied' }
  | { status: 'granted'; coords: Location.LocationObjectCoords }

export function useCurrentLocation() {
  // 追蹤狀態,而不僅僅是座標 — 這樣 UI 可以區分「仍在載入」
  // 與「權限被拒絕」,並顯示可操作的訊息。
  const [state, setState] = useState<LocationState>({ status: 'loading' })

  useEffect(() => {
    let active = true
    ;(async () => {
      const { status } = await Location.requestForegroundPermissionsAsync()
      if (status !== 'granted') {
        if (active) setState({ status: 'denied' })
        return
      }
      const pos = await Location.getCurrentPositionAsync({})
      if (active) setState({ status: 'granted', coords: pos.coords })
    })()
    return () => { active = false }   // 卸載後忽略過時的結果
  }, [])

  return state
}

令牌的安全儲存

import * as SecureStore from 'expo-secure-store'

await SecureStore.setItemAsync('auth_token', token)   // Keychain / Keystore
const token = await SecureStore.getItemAsync('auth_token')

程式碼範例

一個完整的螢幕:路由 → 查詢 → 列表 → 狀態

// app/(tabs)/orders.tsx
import { memo, useCallback } from 'react'
import { FlatList, Text, View } from 'react-native'
import { useQuery } from '@tanstack/react-query'
import { z } from 'zod'

const OrderSchema = z.object({ id: z.string(), total: z.number(), status: z.string() })
const OrdersSchema = z.array(OrderSchema)
type Order = z.infer<typeof OrderSchema>

function useOrders() {
  return useQuery({
    queryKey: ['orders'],
    queryFn: async () => OrdersSchema.parse(await api.listOrders()),
  })
}

// 已記憶化,以便其參考在渲染之間保持穩定(請參閱列表指南)。
const OrderRow = memo(function OrderRow({ item }: { item: Order }) {
  return (
    <View className="px-4 py-3 border-b border-neutral-200">
      <Text className="font-medium">#{item.id}</Text>
      <Text className="text-neutral-500">{item.status} · ${item.total}</Text>
    </View>
  )
})

export default function OrdersScreen() {
  const { data, isLoading, isError, refetch, isRefetching } = useOrders()
  const renderItem = useCallback(({ item }: { item: Order }) => <OrderRow item={item} />, [])

  if (isLoading) return <Centered><Text>載入中…</Text></Centered>
  if (isError) return <Centered><Text accessibilityRole="alert">無法載入訂單。</Text></Centered>
  if (!data?.length) return <Centered><Text>尚無訂單。</Text></Centered>

  return (
    <FlatList
      data={data}
      keyExtractor={(o) => o.id}
      onRefresh={refetch}
      refreshing={isRefetching}
      renderItem={renderItem}
    />
  )
}

一個表單:React Hook Form + Zod 解析器

import { useForm, Controller } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { TextInput, Button, Text } from 'react-native'

const Schema = z.object({ email: z.string().email('無效的電子郵件') })
type FormValues = z.infer<typeof Schema>

export function EmailForm({ onSubmit }: { onSubmit: (v: FormValues) => void }) {
  const { control, handleSubmit, formState: { errors } } = useForm<FormValues>({
    resolver: zodResolver(Schema),
    defaultValues: { email: '' },
  })

  return (
    <>
      <Controller
        control={control}
        name="email"
        render={({ field: { value, onChange, onBlur } }) => (
          <TextInput
            value={value}
            onChangeText={onChange}
            onBlur={onBlur}
            autoCapitalize="none"
            keyboardType="email-address"
            accessibilityLabel="電子郵件地址"
          />
        )}
      />
      {errors.email && <Text accessibilityRole="alert">{errors.email.message}</Text>}
      <Button title="儲存" onPress={handleSubmit(onSubmit)} />
    </>
  )
}

反模式

// 錯誤:在 ScrollView 內部對大型陣列進行 map(無虛擬化、卡頓、高記憶體)
<ScrollView>{items.map((i) => <Row key={i.id} item={i} />)}</ScrollView>
// 正確:FlatList / FlashList

// 錯誤:將伺服器資料複製到用戶端儲存庫(兩個事實來源、資料過時)
const useStore = create((set) => ({ users: [], setUsers: (u) => set({ users: u }) }))
useEffect(() => { getUsers().then(setUsers) }, [])
// 正確:useQuery 擁有伺服器狀態;推導你需要的內容

// 錯誤:令牌放在 AsyncStorage 中(未加密)
await AsyncStorage.setItem('auth_token', token)
// 正確:expo-secure-store

// 錯誤:信任深度連結參數
const { id } = useLocalSearchParams(); fetchUser(id)
// 正確:在使用前使用 Zod 驗證

// 錯誤:在熱路徑上每次渲染都重新建立內聯樣式物件
<View style={{ padding: 16, backgroundColor: '#fff' }} />
// 正確:在模組範圍內使用 StyleSheet.create,或使用 NativeWind className

// 錯誤:真正的秘密隨套件一同發佈
const STRIPE_SECRET = 'sk_live_...'
// 正確:將特權呼叫保留在伺服器端;僅發佈受後端規則保護的公鑰

最佳實務

  • 保持路由檔案精簡;將邏輯放在螢幕元件和 use* 鉤子中。
  • 使用 Zod 驗證每一個外部輸入(API 回應、路由參數、推播負載)。
  • 讓 TanStack Query 擁有伺服器狀態;保持用戶端儲存庫小巧。
  • 始終渲染載入、錯誤和空狀態 — 絕不要只有一個 spinner 而沒有備援。
  • 虛擬化列表;記憶化 renderItem;提供穩定的 keyExtractor。
  • 使用 react-native-reanimated 進行動畫(UI 執行緒);避免在 JS 執行緒上進行繁重工作。
  • 將令牌儲存在 expo-secure-store 中;永遠不要為了授權而信任用戶端。
  • 從一開始就尊重安全區域、動態類型和無障礙角色/標籤。
  • 在發佈前,確認每個原生相依性與新架構的相容性。

相關技能

  • frontend-patterns — React/Next.js(Web)模式;對共用的 React 概念有用,但僅限 DOM 相關。
  • coding-standards — 適用於 RN 程式碼的 TypeScript/JavaScript 慣用語。
  • tdd-workflow、e2e-testing — 測試流程(使用 Jest + React Native Testing Library,RN 使用 Maestro/Detox)。
  • security-review — 一般安全檢查清單,補充上述 RN 套件/秘密指南。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相關技能