RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/Rork入門
Rork入門/2026-03-15上級

React Native + Expo で AI エージェント駆動開発を実践:実装パターン・応用事例・本番運用ガイド

React Native×Expo環境でAIエージェント(Claude等)を活用した高速開発の完全ガイド。実装パターン・コード自動生成・プロンプト工学・本番デプロイ・トラブルシューティングまで網羅。5,000字超の実践的チュートリアル。

React Native209Expo149AI31エージェント3自動化12高速開発プロンプト工学

取り組みの背景:なぜAIエージェント駆動開発が必要なのか

2026年のモバイルアプリ開発は、単なるコーディングの時代から、AIエージェント駆動のAgile開発へ急速に移行しています。特にReact Native + Expo環境では、以下の理由からAIエージェント活用が必須スキルになりました。

従来開発とAIエージェント開発の比較

要素従来開発AIエージェント開発
機能実装時間手動コーディング(4~8時間)プロンプト+検証(30分~2時間)
バグ検出速度ペアプログラミング、レビュー静的解析+AIが自動検証
ドキュメント更新手動、遅延しやすい自動生成、常に最新
リファクタリングエンジニアが計画・実行AIが複数案を提案、選択
学習コスト新しいライブラリ習得に週単位AIが習得を支援、日単位

このプレミアム記事では、Expo環境でのAIエージェント活用を完全にマスターするための実装パターン、プロンプト工学、本番運用テクニック、トラブルシューティングを網羅します。個人開発者からチーム開発まで、実践的なスケーラブルな方法論を解説します。


第1部:基礎概念と環境構築

AIエージェント駆動開発の3つの層

┌─────────────────────────────────────────┐
│ Layer 1: AI指示生成層                   │
│ (プロンプト工学・要件定義)               │
├─────────────────────────────────────────┤
│ Layer 2: コード実装層                   │
│ (スケルトン生成・自動実装・テスト)       │
├─────────────────────────────────────────┤
│ Layer 3: 検証・最適化層                 │
│ (Lint・テスト・パフォーマンス監視)       │
└─────────────────────────────────────────┘

Expo環境の初期セットアップ(AI連携版)

# 1. Expoプロジェクト初期化
npx create-expo-app MyAIApp --template
 
# 2. 必須パッケージインストール
npm install expo-router zustand axios @react-native-async-storage/async-storage
 
# 3. AIエージェント用ディレクトリ構造
mkdir -p {src/{components,hooks,services,utils},prompts,ai-logs}
 
# 4. AI連携設定ファイル
cat > .env << 'ENVEOF'
ANTHROPIC_API_KEY=your_api_key_here
OPENAI_API_KEY=your_key_here
AI_MODEL=claude-3-5-sonnet
LOG_AI_INTERACTIONS=true
ENVEOF

第2部:実装パターン集

パターン1:スケルトン自動生成から実装まで

React NativeコンポーネントをAIエージェントで自動生成する標準的なフロー:

// prompts/component-generator.prompt.txt
// ===== AI指示テンプレート(Claude用) =====
// 以下の仕様でReact Nativeコンポーネントを生成してください:
// - 名前: {ComponentName}
// - 用途: {Purpose}
// - Propsインターフェース: {Props}
// - 要件: TypeScript strict mode対応、accessibility対応
// - スタイル: react-native StyleSheet使用
// - テスト: React Testing Libraryで basic test も生成
 
// ===== 実装例:タスク管理リストコンポーネント =====
 
import React, { useCallback, useState } from 'react';
import {
  View,
  FlatList,
  TouchableOpacity,
  Text,
  StyleSheet,
  Animated,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
 
interface Task {
  id: string;
  title: string;
  completed: boolean;
  dueDate?: string;
  priority: 'low' | 'medium' | 'high';
}
 
interface TaskListProps {
  tasks: Task[];
  onAddTask: () => void;
  onDeleteTask: (id: string) => void;
  onToggleTask: (id: string) => void;
  onEditTask: (id: string, newTitle: string) => void;
}
 
export const TaskList: React.FC<TaskListProps> = ({
  tasks,
  onAddTask,
  onDeleteTask,
  onToggleTask,
  onEditTask,
}) => {
  const [isAnimating] = useState(new Animated.Value(0));
 
  const getPriorityColor = useCallback((priority: Task['priority']) => {
    const colors = {
      low: '#90EE90',
      medium: '#FFD700',
      high: '#FF6347',
    };
    return colors[priority];
  }, []);
 
  const renderTaskItem = ({ item }: { item: Task }) => (
    <Animated.View
      style={[
        styles.taskItem,
        {
          borderLeftColor: getPriorityColor(item.priority),
          borderLeftWidth: 4,
        },
      ]}
    >
      <TouchableOpacity
        onPress={() => onToggleTask(item.id)}
        accessible={true}
        accessibilityLabel={`Toggle ${item.title}`}
      >
        <Ionicons
          name={item.completed ? 'checkmark-circle' : 'ellipse-outline'}
          size={24}
          color={item.completed ? '#4CAF50' : '#999'}
        />
      </TouchableOpacity>
 
      <Text
        style={[
          styles.taskText,
          item.completed && styles.taskTextCompleted,
        ]}
      >
        {item.title}
      </Text>
 
      <TouchableOpacity
        onPress={() => onDeleteTask(item.id)}
        accessible={true}
        accessibilityLabel={`Delete ${item.title}`}
      >
        <Ionicons name="trash-outline" size={20} color="#f44336" />
      </TouchableOpacity>
    </Animated.View>
  );
 
  return (
    <View style={styles.container}>
      <FlatList
        data={tasks}
        renderItem={renderTaskItem}
        keyExtractor={(item) => item.id}
        scrollEnabled={true}
        accessible={true}
        accessibilityLabel="Task list"
      />
 
      <TouchableOpacity
        style={styles.addButton}
        onPress={onAddTask}
        accessible={true}
        accessibilityRole="button"
        accessibilityLabel="Add new task"
      >
        <Ionicons name="add" size={28} color="white" />
      </TouchableOpacity>
    </View>
  );
};
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingHorizontal: 16,
    paddingVertical: 8,
  },
  taskItem: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingHorizontal: 12,
    paddingVertical: 12,
    marginVertical: 6,
    backgroundColor: '#f5f5f5',
    borderRadius: 8,
    gap: 12,
  },
  taskText: {
    flex: 1,
    fontSize: 16,
    color: '#333',
    fontWeight: '500',
  },
  taskTextCompleted: {
    textDecorationLine: 'line-through',
    color: '#999',
  },
  addButton: {
    position: 'absolute',
    bottom: 24,
    right: 24,
    width: 56,
    height: 56,
    borderRadius: 28,
    backgroundColor: '#2196F3',
    justifyContent: 'center',
    alignItems: 'center',
    elevation: 8,
    shadowColor: '#000',
    shadowOpacity: 0.3,
    shadowRadius: 4,
    shadowOffset: { width: 0, height: 4 },
  },
});

パターン2:状態管理のAIアシスト(Zustand)

Zustandストアをエージェントで自動拡張:

// src/store/appStore.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
 
interface AppState {
  // === ユーザー管理 ===
  user: { id: string; name: string; email: string } | null;
  setUser: (user: AppState['user']) => void;
  logout: () => void;
 
  // === タスク管理 ===
  tasks: Task[];
  addTask: (title: string, priority: 'low' | 'medium' | 'high') => void;
  deleteTask: (id: string) => void;
  toggleTask: (id: string) => void;
  updateTaskPriority: (id: string, priority: Task['priority']) => void;
 
  // === UI状態 ===
  isLoading: boolean;
  setLoading: (loading: boolean) => void;
  errorMessage: string | null;
  setError: (error: string | null) => void;
 
  // === 分析・ロギング ===
  analyticsLog: Array<{ timestamp: number; event: string; data: unknown }>;
  logAnalytics: (event: string, data: unknown) => void;
}
 
export const useAppStore = create<AppState>()(
  devtools(
    persist(
      (set, get) => ({
        // === 初期値 ===
        user: null,
        tasks: [],
        isLoading: false,
        errorMessage: null,
        analyticsLog: [],
 
        // === ユーザー操作 ===
        setUser: (user) => set({ user }),
        logout: () => set({ user: null, tasks: [] }),
 
        // === タスク操作 ===
        addTask: (title, priority) => {
          const newTask: Task = {
            id: Date.now().toString(),
            title,
            completed: false,
            priority,
          };
          set((state) => ({ tasks: [...state.tasks, newTask] }));
          get().logAnalytics('task_created', { title, priority });
        },
 
        deleteTask: (id) => {
          set((state) => ({
            tasks: state.tasks.filter((t) => t.id !== id),
          }));
          get().logAnalytics('task_deleted', { id });
        },
 
        toggleTask: (id) => {
          set((state) => ({
            tasks: state.tasks.map((t) =>
              t.id === id ? { ...t, completed: !t.completed } : t
            ),
          }));
        },
 
        updateTaskPriority: (id, priority) => {
          set((state) => ({
            tasks: state.tasks.map((t) =>
              t.id === id ? { ...t, priority } : t
            ),
          }));
        },
 
        // === UI管理 ===
        setLoading: (loading) => set({ isLoading: loading }),
        setError: (error) => set({ errorMessage: error }),
 
        // === 分析 ===
        logAnalytics: (event, data) => {
          set((state) => ({
            analyticsLog: [
              ...state.analyticsLog,
              {
                timestamp: Date.now(),
                event,
                data,
              },
            ],
          }));
        },
      }),
      {
        name: 'app-store',
        storage: {
          getItem: async (name) => {
            const data = await AsyncStorage.getItem(name);
            return data ? JSON.parse(data) : null;
          },
          setItem: async (name, value) => {
            await AsyncStorage.setItem(name, JSON.stringify(value));
          },
          removeItem: async (name) => {
            await AsyncStorage.removeItem(name);
          },
        },
      }
    )
  )
);

第3部:プロンプト工学マスター

効果的なプロンプトの3つの要素

1. 【コンテキスト】: プロジェクト、技術スタック、制約条件
2. 【タスク定義】: 実装する機能、要件、出力形式
3. 【品質基準】: パフォーマンス、テスト、セキュリティ

テンプレート:React Nativeカスタムフック生成

# AI指示テンプレート

## コンテキスト
- フレームワーク: React Native + Expo (SDK 55)
- 状態管理: Zustand
- スタイル: react-native StyleSheet
- テストフレームワーク: Jest + React Testing Library

## タスク
以下の機能を持つカスタムフック `useAsyncData` を生成:
- API呼び出し(キャッシング機能付き)
- ローディング・エラー状態管理
- 自動リトライロジック(3回、指数バックオフ)
- キャンセル可能
- TypeScript strict対応

## 出力形式
- ファイル: `src/hooks/useAsyncData.ts`
- 含めるもの: フック実装、使用例、ユニットテスト(最小限)

## 品質基準
- パフォーマンス: 不要なレンダリング回避(useMemo/useCallback)
- エラーハンドリング: ネットワーク・タイムアウト・キャンセル全て対応
- 保守性: インラインコメント3行以上、複雑な部分は詳しく

実装されたフック例(AIが生成したもの)

// src/hooks/useAsyncData.ts
import { useCallback, useEffect, useRef, useState } from 'react';
 
interface UseAsyncDataState<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
}
 
interface UseAsyncDataOptions {
  cacheTime?: number; // ms (default: 5min)
  retries?: number; // (default: 3)
}
 
export function useAsyncData<T>(
  asyncFn: () => Promise<T>,
  deps: React.DependencyList = [],
  options: UseAsyncDataOptions = {}
): UseAsyncDataState<T> {
  const { cacheTime = 5 * 60 * 1000, retries = 3 } = options;
 
  const [state, setState] = useState<UseAsyncDataState<T>>({
    data: null,
    loading: true,
    error: null,
  });
 
  const cacheRef = useRef<{ data: T; timestamp: number } | null>(null);
  const abortControllerRef = useRef<AbortController | null>(null);
 
  const fetchData = useCallback(async () => {
    const now = Date.now();
 
    // キャッシュチェック
    if (
      cacheRef.current &&
      now - cacheRef.current.timestamp < cacheTime
    ) {
      setState({
        data: cacheRef.current.data,
        loading: false,
        error: null,
      });
      return;
    }
 
    // 新規リクエスト
    setState((prev) => ({ ...prev, loading: true }));
    abortControllerRef.current = new AbortController();
 
    let lastError: Error | null = null;
 
    for (let attempt = 0; attempt < retries; attempt++) {
      try {
        const data = await asyncFn();
 
        // キャッシュ保存
        cacheRef.current = { data, timestamp: now };
 
        setState({
          data,
          loading: false,
          error: null,
        });
        return;
      } catch (err) {
        lastError = err instanceof Error ? err : new Error(String(err));
 
        // 指数バックオフ: 1s, 2s, 4s
        const backoffMs = 1000 * Math.pow(2, attempt);
        await new Promise((resolve) =>
          setTimeout(resolve, backoffMs)
        );
      }
    }
 
    // 全リトライ失敗
    setState({
      data: null,
      loading: false,
      error: lastError || new Error('Unknown error'),
    });
  }, [asyncFn, cacheTime, retries]);
 
  useEffect(() => {
    fetchData();
 
    return () => {
      abortControllerRef.current?.abort();
    };
  }, deps);
 
  return state;
}

第4部:本番運用とトラブルシューティング

エラーパターン集と対処法

// src/utils/errorHandler.ts
 
interface ErrorContext {
  component: string;
  action: string;
  severity: 'low' | 'medium' | 'high' | 'critical';
  context?: Record<string, unknown>;
}
 
enum ErrorCode {
  NETWORK_TIMEOUT = 'E_NETWORK_TIMEOUT',
  AUTH_FAILED = 'E_AUTH_FAILED',
  PARSE_ERROR = 'E_PARSE_ERROR',
  NATIVE_MODULE_ERROR = 'E_NATIVE_MODULE',
}
 
export class AppError extends Error {
  constructor(
    code: ErrorCode,
    message: string,
    context: ErrorContext
  ) {
    super(message);
    this.code = code;
    this.context = context;
    this.timestamp = Date.now();
  }
 
  code: ErrorCode;
  context: ErrorContext;
  timestamp: number;
 
  toJSON() {
    return {
      code: this.code,
      message: this.message,
      context: this.context,
      timestamp: this.timestamp,
    };
  }
}
 
// 使用例
try {
  const response = await fetch('https://api.example.com/data', {
    timeout: 5000,
  });
  if (!response.ok) throw new Error('API error');
  return await response.json();
} catch (err) {
  throw new AppError(
    ErrorCode.NETWORK_TIMEOUT,
    'Failed to fetch data',
    {
      component: 'DataService',
      action: 'fetchData',
      severity: 'high',
      context: { endpoint: 'api.example.com' },
    }
  );
}

デプロイ前チェックリスト(AI検証対応)

# チェック1: TypeScript厳密性チェック
npx tsc --strict --noEmit
 
# チェック2: Linting
npm run lint -- --strict-rule-severity
 
# チェック3: テスト実行
npm test -- --coverage --threshold 80
 
# チェック4: パフォーマンス測定
npx react-native-performance-monitor --baseline
 
# チェック5: セキュリティスキャン
npm audit --audit-level=moderate
 
# チェック6: EAS Build準備
eas build:configure
 
# チェック7: AI連携ログクリア
rm -rf ai-logs/*

まとめ:AIエージェント駆動開発の未来

React Native + Expo環境でのAIエージェント駆動開発は、以下の利点をもたらします。

  • 開発速度: 従来比3~5倍高速化
  • 品質: 自動テスト・静的解析により人為的ミス削減
  • 学習効率: エンジニアが概念設計に集中可能
  • スケーラビリティ: チームの生産性向上

本記事で紹介した3つの層の実装パターン、プロンプト工学、本番運用テクニックを組み合わせることで、個人開発者でも大規模アプリを効率的に開発・保守できる時代が到来しています。

AIエージェントをパートナーとして、次世代のモバイルアプリ開発に挑戦してください。

12年の個人開発で見えてきたこと

リリース直後にチェックしているもの

  • クラッシュレポートの上位エラーを24時間ごとに確認しているか
  • AdMob/StoreKit の収益ダッシュボードに異常検知が出ていないか
  • ユーザーレビューに技術的な指摘が含まれていないか
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Rork 入門2026-05-05
ネイティブアプリと PWA、どちらを選ぶべきか — Rork を使う前に整理しておきたい3つの判断軸
ネイティブアプリと PWA の違いを機能差で整理し、Rork でアプリを作るべきかを判断できます。プッシュ通知・カメラ・App Store 公開など具体的なコード例と判断基準を紹介します。
Rork 入門2026-04-02
Rork アプリのスプラッシュスクリーンをカスタマイズする — expo-splash-screen で起動画面を演出する方法
Rork × Expo アプリのスプラッシュスクリーン(起動画面)をカスタマイズする方法を解説。expo-splash-screen を使ったロゴ表示、背景色設定、非同期処理完了まで画面を保持するテクニックを初心者向けに丁寧に説明します。
Rork 入門2026-03-28
Rork アプリの UX デザインパターン — ユーザー体験を劇的に向上させる画面設計・インタラクション・マイクロアニメーションの実践テクニック
Rork で作るアプリのUXを劇的に改善する実践ガイド。画面レイアウト設計、ナビゲーション構造、マイクロインタラクション、オンボーディングフロー、アクセシビリティまで、プロ品質のアプリに仕上げるデザインパターンを体系的に解説します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →