Communitygithub.com

react-native-patterns

React Native および Expo アプリのパターン集 — Expo Router によるナビゲーション、状態の分離(サーバー/クライアント/ルート/フォーム)、Zod を用いた TanStack Query によるデータフェッチ、パフォーマンスの良いリスト、NativeWind/StyleSheet によるスタイリング、ネイティブ API、セキュアストレージ。React Native / Expo の画面、コンポーネント、ナビゲーション、データレイヤーの構築や編集時に使用します。

react-native-patterns とは?

このスキルは、`react-native-patterns` という名前で、Expo を使用したプロダクション向け React Native アプリケーションを構築するための実践的なパターンを提供します。このスキルがカバーする範囲は、Expo Router を用いたナビゲーション(ルートパラメータのZodバリデーション)、TanStack Query + Zod を用いたデータフェッチ、サーバー状態・クライアント状態・ルート状態・フォーム状態の適切な分離、FlatList/FlashList による仮想化リスト、NativeWind または StyleSheet によるスタイリング、そして Expo のネイティブ API(位置情報、カメラなど)や `expo-secure-store` を利用したセキュアストレージです。このスキルは、Claude Code などの AI エージェント用に設計されており、SKILL.md には具体的なコード例、アンチパターン、ベストプラクティスが豊富に含まれ、開発者が React Native アプリケーションを正しく、効率的に構築できるよう支援します。

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

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

React Native / Expo パターン集

プロダクション向け React Native アプリを Expo で構築するための実践的なパターン。ナビゲーション、状態管理、データフェッチ、リスト、スタイリング、ネイティブ API をカバーします。rules/react-native/ ルールセットと組み合わせて使用します。ルールは 何を 強制するかを示し、このスキルは どのように 実装するかを示します。

以下に挙げるライブラリ(NativeWind、Zustand/Jotai、TanStack Query)は、よく使われる確立されたオプションを例として示したものです。パターン自体が重要であり、特定のパッケージではありません。同等の機能を持つ別のライブラリでも問題ありません。Zod は、ECC の既存の typescript/ ルールと一貫性を保つためにバリデーションに使用しています。

これらのパターンは、Managed Expo ワークフロー(Expo Router、EAS、expo-* モジュール)を前提とし、新しいアーキテクチャ(最近の Expo SDK ではデフォルト、SDK 55 以降必須)上で動作します。ブラウザの DOM(<div>、URL バー、Web のデータフェッチのデフォルト動作)は前提としていません。

アクティベーション条件

以下の場合にこのスキルを有効にしてください:

  • React Native / Expo の画面、コンポーネント、ナビゲーションを構築または編集する
  • Expo Router(ファイルベースの app/ ディレクトリ)でルーティングを設定する
  • 状態の配置場所(サーバーキャッシュ vs クライアントストア vs ルートパラメータ vs フォーム)を決める
  • TanStack Query でデータフェッチを配線し、Zod でレスポンスをバリデーションする
  • 長いリストや重いリストをレンダリングする
  • スタイリング手法(NativeWind または StyleSheet)を選択または適用する
  • ネイティブデバイス API(カメラ、位置情報、通知)やセキュアストレージにアクセスする
  • RN コードをモバイル特有の問題についてレビューする

ここでは Web/React-DOM のパターン(URL を状態として扱う、<div>、SWR ブラウザ向け)は使用しないでください。これらは 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

useEffect 内での fetch ではなく、サーバーキャッシュライブラリ(TanStack Query、SWR)を使用します。境界でバリデーションを行い、スキーマから型を推論します。ローディング、エラー、空の状態を明示的に処理します(例では 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>Loading…</Text></Centered>
  if (isError) return <Centered><Text accessibilityRole="alert">Could not load orders.</Text></Centered>
  if (!data?.length) return <Centered><Text>No orders yet.</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('Invalid 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="Email address"
          />
        )}
      />
      {errors.email && <Text accessibilityRole="alert">{errors.email.message}</Text>}
      <Button title="Save" onPress={handleSubmit(onSubmit)} />
    </>
  )
}

アンチパターン

// 間違い:ScrollView 内で大きな配列を map している(仮想化なし、カクカク、メモリ大量消費)
<ScrollView>{items.map((i) => <Row key={i.id} item={i} />)}</ScrollView>
// 正しい:FlatList / FlashList

// 間違い:サーバーデータをクライアントストアにコピー(2つの情報源、古いデータ)
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* フックに配置する
  • すべての外部入力(API レスポンス、ルートパラメータ、プッシュペイロード)を Zod でバリデーションする
  • TanStack Query にサーバー状態を任せ、クライアントストアは小さく保つ
  • ローディング、エラー、空の状態を常にレンダリングする — フォールバックなしのスピナーだけは避ける
  • リストは仮想化し、renderItem はメモ化し、安定した keyExtractor を提供する
  • アニメーションには react-native-reanimated(UI スレッド)を使用し、JS スレッドでの重い処理は避ける
  • トークンは expo-secure-store に保存し、クライアントでの認可処理は決して信用しない
  • セーフエリア、Dynamic Type、アクセシビリティのロール/ラベルを最初から尊重する
  • リリース前にすべてのネイティブ依存関係の New Architecture 互換性を確認する

関連スキル

  • frontend-patterns — React/Next.js(Web)パターン。共通の React 概念に役立つが、DOM 固有の部分を含む
  • coding-standards — RN コードに適用される TypeScript/JavaScript イディオム
  • tdd-workflow、e2e-testing — テストプロセス(Jest + React Native Testing Library、Maestro/Detox for RN を使用)
  • 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.

関連スキル