はじめに
CursorなどのAIエディタを使ってフロントエンド開発をしていると、「機能は動いているけれど、裏でAPIが無限に呼ばれている」「モーダルを開閉するだけで画面が変にカクつく」といったレンダリングやパフォーマンスのバグに遭遇することはありませんか?
バックエンドのロジックと違い、Reactなどのフロントエンド実装は「ライフサイクル」や「再レンダリングのトリガー」をAIが正しく文脈として理解していないと、「とりあえず動くけど品質が低いコード」 が量産されがちです。
この記事では、実際の開発で発生したバグ事例を元に、AIにフロントエンド実装を任せる際に 「これだけは意識してもらいたい基本ルール」 と、それをCursorのルールファイル(.cursor/rules)に落とし込む方法を紹介します。
よくある「AI実装の落とし穴」3選
1. useEffect の依存配列にオブジェクトをそのまま突っ込む
AIは「データが変わったら処理を実行して」という指示を受けると、一番手っ取り早い useEffect を使いがちです。その際、依存配列にオブジェクトそのものを指定してしまうことが多々あります。
// 🚫 AIがやりがちな惜しい実装
useEffect(() => {
updateForm(data);
}, [data]); // dataがオブジェクトの場合、中身が同じでも参照が変わるたびに実行されてしまう!
これだと、親コンポーネントが再レンダリングされるたびに useEffect が発火してしまい、無限ループや不要な処理の原因になります。
2. APIリクエストの重複(手動 refetch vs invalidateQueries)
React Query (TanStack Query) などのライブラリを使っている場合、データの更新後は invalidateQueries でキャッシュを無効化して最新データを取得するのが定石ですよね。
しかし、AIに「モーダルを閉じたら一覧を更新して」と指示すると、コンポーネント内で手動で refetch() を呼ぶコードを勝手に追加してしまうことがあります。結果として、「invalidateQueries による自動再取得」と「手動 refetch」が同時に走り、APIリクエストが2倍飛ぶというもったいない状態になります。
3. エラーハンドリングの競合
「エラー時はモーダルを出して」と指示すると、AIは親切にコンポーネント内へ個別のエラー表示ロジックを組んでくれます。
ただ、プロジェクト全体で Axios の Interceptor などを使ったグローバルなエラーハンドリングを既に設定している場合、「グローバルなエラーモーダル」と「個別のエラーメッセージ」が二重に表示されるといったUI崩れが起きてしまいます。
AIと上手く付き合うための「Cursor Rules」設定
こうした問題を防ぐためには、AIを毎回レビューで指摘するのではなく、Cursorの .cursor/rules フォルダにフロントエンド専用のルールファイル(例: frontend-react.mdc)を作って、事前に読ませておくのが一番効果的です。
以下は、実際のプロジェクトで効果があったルール設定のサンプルです。ぜひコピペして使ってみてください。
.cursor/rules/frontend-react.mdc サンプル
---
description: React/Next.js フロントエンド実装時のルール
globs: **/*.tsx, **/*.ts
---
# React フロントエンド実装ルール
Reactコンポーネントを実装・修正する際は、以下のパフォーマンスと設計ルールを意識して実装してください。
## 1. レンダリング最適化とHooks
- **useEffectの依存配列**:
- オブジェクトや配列を直接依存配列に入れないでください。
- 必ず `obj.id` や `value` などのプリミティブな値を指定するか、必要に応じて `JSON.stringify` 等で比較してください。
- `useMemo` や `useCallback` を活用し、参照の安定性を確保することを優先してください。
- **不要なuseEffectの回避**:
- `props` から `state` への同期(派生状態の作成)に `useEffect` を使わないでください。
- 計算可能な値はレンダリング中に直接計算するか、`useMemo` を使用してください。
## 2. データフェッチ (React Query / TanStack Query)
- **再取得の戦略**:
- 更新系処理(Mutation)の後は、原則として `queryClient.invalidateQueries` を使用してキャッシュを無効化してください。
- コンポーネント側で手動の `refetch()` を `useEffect` やイベントハンドラで呼ばないでください(二重リクエストを防止するため)。
## 3. エラーハンドリング
- **二重表示の防止**:
- グローバルなエラーハンドリング(Interceptor等)が存在することを前提とし、個別の `try-catch` でエラーを不必要に握りつぶさないでください。
- 特定のUIにのみエラーを表示したい場合は、グローバル通知を抑制する仕組み(`suppress` オプション等)を考慮してください。
## 4. コンポーネント設計
- **フォーム実装**:
- 巨大なフォームで `useState` を多用せず、可能な限り `react-hook-form` などのライブラリを使用するか、状態を適切に分割してください。
- モーダルなどの開閉状態は、不要な再レンダリングを防ぐため、適切な階層の親コンポーネントで管理してください。
さらに精度を上げる「プロンプト」のコツ
ルールファイルを設定した上で、チャットで指示を出す際も少しだけ解像度を上げて伝えると、一発で綺麗なコードを出してくれます。
惜しい指示の例
「ユーザー編集モーダルを作って。更新したら一覧をリロードして。」
おすすめの指示の例
「ユーザー編集モーダルを作成してください。
更新処理後は React Query のinvalidateQueriesを使って一覧を更新し、手動でのrefetchは実装しないでください。
また、useEffectでフォーム初期値をセットする際は、オブジェクトの参照変更による不要な再レンダリングが起きないよう注意してください。」
まとめ
AIは「動くコード」をスピーディに書くのは得意ですが、「無駄のない綺麗なコード」を書くにはまだ人間のガイドが必要です。特にReactのレンダリング挙動は、人間でもうっかりミスしやすいポイントですよね。
useEffectの依存配列のルールを明文化する- データ取得ライブラリの作法(
invalidateQueries等)を事前共有する - エラーハンドリングの競合を防ぐルールを入れる
これらを .cursor/rules に仕込んでおくだけで、AIとのペアプログラミングが驚くほどスムーズになり、コードレビューの手間もガクッと減ります。Cursorを使っている方は、ぜひプロジェクトに導入してみてください!