react-native
React NativeとExpoを使ったパフォーマンス重視のモバイルアプリ開発パターンを提供します。リストのパフォーマンス最適化、Reanimatedによるアニメーション、ナビゲーション、UIパターン、状態管理、プラットフォーム固有のコード、Expoワークフローをカバーします。React Nativeコードの実装やレビュー時、またはiOS・Androidアプリ開発全般に活用できます。
description の原文を見る
React Native and Expo patterns for building performant mobile apps. Covers list performance, animations with Reanimated, navigation, UI patterns, state management, platform-specific code, and Expo workflows. Use when building or reviewing React Native code. Triggers: 'react native', 'expo', 'mobile app', 'react native performance', 'flatlist', 'reanimated', 'expo router', 'mobile development', 'ios app', 'android app'.
SKILL.md 本文
React Native パターン
React Native + Expo アプリ向けパフォーマンス・アーキテクチャパターン。ルールは影響度でランク付けされています — MEDIUM に手をつける前に CRITICAL を修正してください。
これは出発点です。モバイルアプリをより多く構築するにつれ、このスキルは成長します。
適用時期
- 新しい React Native または Expo アプリを構築する
- リストとスクロールのパフォーマンスを最適化する
- アニメーションを実装する
- モバイルコードのパフォーマンス問題をレビューする
- 新しい Expo プロジェクトをセットアップする
1. リストパフォーマンス (CRITICAL)
リストは React Native の #1 パフォーマンス問題です。ぎくしゃくしたスクロールはアプリ全体の体験を台無しにします。
| パターン | 問題 | 修正方法 |
|---|---|---|
| データ用 ScrollView | <ScrollView> はすべてのアイテムを一度にレンダリング | <FlatList> または <FlashList> を使用 — 仮想化され、表示されているアイテムのみレンダリング |
| keyExtractor がない | keyExtractor なしの FlatList → 不要な再レンダリング | keyExtractor={(item) => item.id} — アイテムごとに安定したユニークキー |
| 複雑な renderItem | renderItem の高コストコンポーネントがスクロールのたびに再レンダリング | React.memo でラップ、別のコンポーネントに抽出 |
| renderItem のインライン関数 | renderItem={({ item }) => <Row onPress={() => nav(item.id)} />} | ハンドラを抽出: const handlePress = useCallback(...) |
| getItemLayout なし | FlatList はスクロール時すべてのアイテムを計測 (高コスト) | 固定高さのアイテムに getItemLayout を提供: (data, index) => ({ length: 80, offset: 80 * index, index }) |
| FlashList | FlatList は良いが、大きなリストでは FlashList がさらに良い | @shopify/flash-list — ドロップイン替え、リサイクルアーキテクチャ |
| リスト内の大きな画像 | フルレゾリューション画像がメインスレッドでデコード | expo-image をプレースホルダ + トランジション付きで使用、サイズを指定 |
FlatList チェックリスト
すべての FlatList は以下を含む必要があります:
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={renderItem} // メモ化されたコンポーネント
getItemLayout={getItemLayout} // アイテムが固定高さの場合
initialNumToRender={10} // マウント時に 100 個のアイテムをレンダリングしない
maxToRenderPerBatch={10} // オフスクリーンレンダリングのバッチサイズ
windowSize={5} // メモリに保持するスクリーン数
removeClippedSubviews={true} // オフスクリーンアイテムをアンマウント (Android)
/>
2. アニメーション (HIGH)
ネイティブアニメーションは UI スレッドで実行されます。JS アニメーションは JS スレッドをブロックしてジャンクを引き起こします。
| パターン | 問題 | 修正方法 |
|---|---|---|
| 複雑なアニメーション用 Animated API | Animated は JS スレッドで実行され、インタラクションをブロック | react-native-reanimated を使用 — UI スレッドで実行 |
| レイアウトアニメーション | アイテムが遷移なしで表示/消失 | LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) |
| 共有要素トランジション | スクリーン間をナビゲート、要素がテレポート | react-native-reanimated 共有トランジションまたは expo-router 共有要素 |
| ジェスチャ + アニメーション | ドラッグ/スワイプが遅い | react-native-gesture-handler + reanimated worklets — すべて UI スレッドで |
| レイアウト計測 | onLayout が遅く発火、フラッシュの原因に | 共有値で useAnimatedStyle を使用して即座に応答 |
Reanimated の基本
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
function AnimatedBox() {
const offset = useSharedValue(0);
const style = useAnimatedStyle(() => ({
transform: [{ translateX: withSpring(offset.value) }],
}));
return (
<GestureDetector gesture={panGesture}>
<Animated.View style={[styles.box, style]} />
</GestureDetector>
);
}
3. ナビゲーション (HIGH)
| パターン | 問題 | 修正方法 |
|---|---|---|
| Expo Router | ファイルベースのルーティング (Next.js のような) React Native 向け | app/ ディレクトリと _layout.tsx ファイル。新しい Expo プロジェクト向けに推奨。 |
| スタック上の重いスクリーン | すべてのスクリーンがスタックにマウント状態 | 永続化が不要なスクリーンに unmountOnBlur: true を使用 |
| ディープリンキング | アプリが URL に応答しない | Expo Router が自動的に処理。ベア RN の場合: Linking API 設定 |
| タブバッジ更新 | タブがフォーカスされてもバッジ数が更新されない | useIsFocused() を使用またはフォーカス時に再フェッチ: useFocusEffect(useCallback(...)) |
| ナビゲーション状態の永続化 | バックグラウンド/キル時にアプリが位置を失う | onStateChange + initialState と AsyncStorage |
Expo Router 構造
app/
├── _layout.tsx # ルートレイアウト (タブナビゲータ)
├── index.tsx # ホームタブ
├── (tabs)/
│ ├── _layout.tsx # タブバー設定
│ ├── home.tsx
│ ├── search.tsx
│ └── profile.tsx
├── [id].tsx # 動的ルート
└── modal.tsx # モーダルルート
4. UI パターン (HIGH)
| パターン | 問題 | 修正方法 |
|---|---|---|
| セーフエリア | ノッチやホームインジケーター下のコンテンツ | <SafeAreaView> または react-native-safe-area-context の useSafeAreaInsets() |
| キーボード回避 | フォームフィールドがキーボードの背後に隠れる | <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'}> |
| プラットフォーム固有コード | iOS と Android が異なる動作を必要とする | Platform.select({ ios: ..., android: ... }) またはファイル .ios.tsx / .android.tsx |
| ステータスバー | ステータスバーがコンテンツと重なるか色が間違う | expo-status-bar の <StatusBar style="auto" /> をルートレイアウトに |
| タッチターゲット | ボタンがタップするには小さすぎる | 最小 44x44pt。hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} を使用 |
| ハプティクフィードバック | タップが反応なく感じる | expo-haptics — 重要なアクションで Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light) |
5. 画像とメディア (MEDIUM)
| パターン | 問題 | 修正方法 |
|---|---|---|
| Image コンポーネント | react-native の <Image> は基本的 | expo-image を使用 — キャッシング、プレースホルダ、トランジション、blurhash |
| サイズなしのリモート画像 | 画像読み込み時にレイアウトシフト | 常に width と height を指定、または aspectRatio を使用 |
| 大きな画像 | Android でOOM クラッシュ | サーバー側でリサイズするか、メモリを処理する expo-image を使用 |
| SVG | SVG サポートはネイティブではない | react-native-svg + SVG インポート用 react-native-svg-transformer |
| ビデオ | ビデオ再生 | expo-av または expo-video (より新しい API) |
6. 状態とデータ (MEDIUM)
| パターン | 問題 | 修正方法 |
|---|---|---|
| 複雑なデータ用 AsyncStorage | すべての読み込みで JSON パース/文字列化 | MMKV (react-native-mmkv) を使用 — AsyncStorage より 30 倍高速 |
| グローバル状態 | シンプルな状態に Redux/MobX ボイラープレート | Zustand — ミニマル、React Native で素晴らしく機能 |
| サーバー状態 | 手動フェッチ + ローディング + エラー + キャッシュ | TanStack Query — Web と同じ、React Native で機能 |
| オフラインファースト | ネットワークなしでアプリが使用不可 | TanStack Query persistQueryClient + MMKV、または複雑なオフライン用 WatermelonDB |
| 深いステート更新 | ネストオブジェクト用スプレッド演算子の地獄 | Zustand 経由の Immer: set(produce(state => { state.user.name = 'new' })) |
7. Expo ワークフロー (MEDIUM)
| パターン | いつ | 方法 |
|---|---|---|
| 開発ビルド | ネイティブモジュールが必要 | npx expo run:ios または eas build --profile development |
| Expo Go | 素早いプロトタイピング、ネイティブモジュール不要 | npx expo start — QR コードをスキャン |
| EAS Build | CI/CD、App Store ビルド | eas build --platform ios --profile production |
| EAS Update | App Store レビューなしのホットフィックス | eas update --branch production --message "Fix bug" |
| Config プラグイン | エジェクトなしでネイティブ設定を変更 | app.config.ts と expo-build-properties またはカスタム config プラグイン |
| 環境変数 | ビルドごとの異なる設定 | eas.json ビルドプロフィール + expo-constants |
新規プロジェクトセットアップ
npx create-expo-app my-app --template tabs
cd my-app
npx expo install expo-image react-native-reanimated react-native-gesture-handler react-native-safe-area-context
8. テスト (LOW-MEDIUM)
| ツール | 用途 | セットアップ |
|---|---|---|
| Jest | ユニットテスト、フックテスト | デフォルトで Expo に含まれる |
| React Native Testing Library | コンポーネントテスト | @testing-library/react-native |
| Detox | 実デバイス/シミュレータ上の E2E テスト | detox — Wix のテスティングフレームワーク |
| Maestro | YAML フロー付き E2E | maestro test flow.yaml — Detox より簡単 |
よくある落とし穴
| 落とし穴 | 修正方法 |
|---|---|
| Metro バンドラーキャッシュ | npx expo start --clear |
| Pod インストール問題 (iOS) | cd ios && pod install --repo-update |
| Reanimated が機能しない | ルート内で最初のインポートである必要があります: import 'react-native-reanimated' |
| Expo SDK アップグレード | SDK バージョン更新後 npx expo install --fix |
| Android ビルド失敗 | gradle.properties でメモリをチェック: org.gradle.jvmargs=-Xmx4g |
| iOS シミュレータが遅い | パフォーマンステスト用は物理デバイスを使用 — シミュレータは実際のパフォーマンスを反映しません |
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- jezweb
- リポジトリ
- jezweb/claude-skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/jezweb/claude-skills / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。